planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

22
node_modules/decap-cms-lib-auth/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2016 Netlify <decap@p-m.si>
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
node_modules/decap-cms-lib-auth/README.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Lib Auth
Shared components to handle OAuth and implicit authentication flows.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _immutable = require("immutable");
var _trim = _interopRequireDefault(require("lodash/trim"));
var _trimEnd = _interopRequireDefault(require("lodash/trimEnd"));
var _utils = require("./utils");
const _excluded = ["access_token"];
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
class ImplicitAuthenticator {
constructor(config = {}) {
const baseURL = (0, _trimEnd.default)(config.base_url, '/');
const authEndpoint = (0, _trim.default)(config.auth_endpoint, '/');
this.auth_url = `${baseURL}/${authEndpoint}`;
this.appID = config.app_id;
this.clearHash = config.clearHash;
}
authenticate(options, cb) {
if ((0, _utils.isInsecureProtocol)()) {
return cb(new Error('Cannot authenticate over insecure protocol!'));
}
const authURL = new URL(this.auth_url);
authURL.searchParams.set('client_id', this.appID);
authURL.searchParams.set('redirect_uri', document.location.origin + document.location.pathname);
authURL.searchParams.set('response_type', 'token');
authURL.searchParams.set('scope', options.scope);
if (options.prompt != null && options.prompt != undefined) {
authURL.searchParams.set('prompt', options.prompt);
}
if (options.resource != null && options.resource != undefined) {
authURL.searchParams.set('resource', options.resource);
}
const state = JSON.stringify({
auth_type: 'implicit',
nonce: (0, _utils.createNonce)()
});
authURL.searchParams.set('state', state);
document.location.assign(authURL.href);
}
/**
* Complete authentication if we were redirected back to from the provider.
*/
completeAuth(cb) {
const hashParams = new URLSearchParams(document.location.hash.replace(/^#?\/?/, ''));
if (!hashParams.has('access_token') && !hashParams.has('error')) {
return;
}
// Remove tokens from hash so that token does not remain in browser history.
this.clearHash();
const params = (0, _immutable.Map)(hashParams.entries());
const {
nonce
} = JSON.parse(params.get('state'));
const validNonce = (0, _utils.validateNonce)(nonce);
if (!validNonce) {
return cb(new Error('Invalid nonce'));
}
if (params.has('error')) {
return cb(new Error(`${params.get('error')}: ${params.get('error_description')}`));
}
if (params.has('access_token')) {
const _params$toJS = params.toJS(),
{
access_token: token
} = _params$toJS,
data = _objectWithoutProperties(_params$toJS, _excluded);
cb(null, _objectSpread({
token
}, data));
}
}
}
exports.default = ImplicitAuthenticator;

33
node_modules/decap-cms-lib-auth/dist/esm/index.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DecapCmsLibAuth = void 0;
Object.defineProperty(exports, "ImplicitAuthenticator", {
enumerable: true,
get: function () {
return _implicitOauth.default;
}
});
Object.defineProperty(exports, "NetlifyAuthenticator", {
enumerable: true,
get: function () {
return _netlifyAuth.default;
}
});
Object.defineProperty(exports, "PkceAuthenticator", {
enumerable: true,
get: function () {
return _pkceOauth.default;
}
});
var _netlifyAuth = _interopRequireDefault(require("./netlify-auth"));
var _implicitOauth = _interopRequireDefault(require("./implicit-oauth"));
var _pkceOauth = _interopRequireDefault(require("./pkce-oauth"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const DecapCmsLibAuth = exports.DecapCmsLibAuth = {
NetlifyAuthenticator: _netlifyAuth.default,
ImplicitAuthenticator: _implicitOauth.default,
PkceAuthenticator: _pkceOauth.default
};

View File

@@ -0,0 +1,148 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _trim = _interopRequireDefault(require("lodash/trim"));
var _trimEnd = _interopRequireDefault(require("lodash/trimEnd"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const NETLIFY_API = 'https://api.netlify.com';
const AUTH_ENDPOINT = 'auth';
class NetlifyError {
constructor(err) {
this.err = err;
}
toString() {
return this.err && this.err.message;
}
}
const PROVIDERS = {
github: {
width: 960,
height: 600
},
gitlab: {
width: 960,
height: 600
},
bitbucket: {
width: 960,
height: 500
},
email: {
width: 500,
height: 400
}
};
class Authenticator {
constructor(config = {}) {
this.site_id = config.site_id || null;
this.base_url = (0, _trimEnd.default)(config.base_url, '/') || NETLIFY_API;
this.auth_endpoint = (0, _trim.default)(config.auth_endpoint, '/') || AUTH_ENDPOINT;
}
handshakeCallback(options, cb) {
const fn = e => {
if (e.data === 'authorizing:' + options.provider && e.origin === this.base_url) {
window.removeEventListener('message', fn, false);
window.addEventListener('message', this.authorizeCallback(options, cb), false);
return this.authWindow.postMessage(e.data, e.origin);
}
};
return fn;
}
authorizeCallback(options, cb) {
const fn = e => {
if (e.origin !== this.base_url) {
return;
}
if (e.data.indexOf('authorization:' + options.provider + ':success:') === 0) {
const data = JSON.parse(e.data.match(new RegExp('^authorization:' + options.provider + ':success:(.+)$'))[1]);
window.removeEventListener('message', fn, false);
this.authWindow.close();
cb(null, data);
}
if (e.data.indexOf('authorization:' + options.provider + ':error:') === 0) {
const err = JSON.parse(e.data.match(new RegExp('^authorization:' + options.provider + ':error:(.+)$'))[1]);
window.removeEventListener('message', fn, false);
this.authWindow.close();
cb(new NetlifyError(err));
}
};
return fn;
}
getSiteID() {
if (this.site_id) {
return this.site_id;
}
const host = document.location.host.split(':')[0];
return host === 'localhost' ? 'demo.decapcms.org' : host;
}
authenticate(options, cb) {
const {
provider
} = options;
const siteID = this.getSiteID();
if (!provider) {
return cb(new NetlifyError({
message: 'You must specify a provider when calling netlify.authenticate'
}));
}
if (!siteID) {
return cb(new NetlifyError({
message: "You must set a site_id with netlify.configure({site_id: 'your-site-id'}) to make authentication work from localhost"
}));
}
const conf = PROVIDERS[provider] || PROVIDERS.github;
const left = screen.width / 2 - conf.width / 2;
const top = screen.height / 2 - conf.height / 2;
window.addEventListener('message', this.handshakeCallback(options, cb), false);
let url = `${this.base_url}/${this.auth_endpoint}?provider=${options.provider}&site_id=${siteID}`;
if (options.scope) {
url += '&scope=' + options.scope;
}
if (options.login === true) {
url += '&login=true';
}
if (options.beta_invite) {
url += '&beta_invite=' + options.beta_invite;
}
if (options.invite_code) {
url += '&invite_code=' + options.invite_code;
}
this.authWindow = window.open(url, 'Netlify Authorization', `width=${conf.width}, height=${conf.height}, top=${top}, left=${left}`);
this.authWindow.focus();
}
refresh(options, cb) {
const {
provider,
refresh_token
} = options;
const siteID = this.getSiteID();
const onError = cb || Promise.reject.bind(Promise);
if (!provider || !refresh_token) {
return onError(new NetlifyError({
message: 'You must specify a provider and refresh token when calling netlify.refresh'
}));
}
if (!siteID) {
return onError(new NetlifyError({
message: "You must set a site_id with netlify.configure({site_id: 'your-site-id'}) to make token refresh work from localhost"
}));
}
const url = `${this.base_url}/${this.auth_endpoint}/refresh?provider=${provider}&site_id=${siteID}&refresh_token=${refresh_token}`;
const refreshPromise = fetch(url, {
method: 'POST',
body: ''
}).then(res => res.json());
// Return a promise if a callback wasn't provided
if (!cb) {
return refreshPromise;
}
// Otherwise, use the provided callback.
refreshPromise.then(data => cb(null, data)).catch(cb);
}
}
var _default = exports.default = Authenticator;

133
node_modules/decap-cms-lib-auth/dist/esm/pkce-oauth.js generated vendored Normal file
View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _trim = _interopRequireDefault(require("lodash/trim"));
var _trimEnd = _interopRequireDefault(require("lodash/trimEnd"));
var _utils = require("./utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
async function sha256(text) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
const digest = await window.crypto.subtle.digest('SHA-256', data);
const sha = String.fromCharCode(...new Uint8Array(digest));
return sha;
}
// based on https://github.com/auth0/auth0-spa-js/blob/9a83f698127eae7da72691b0d4b1b847567687e3/src/utils.ts#L147
function generateVerifierCode() {
// characters that can be used for codeVerifier
// excludes _~ as if included would cause an uneven distribution as char.length would no longer be a factor of 256
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.';
const randomValues = Array.from(window.crypto.getRandomValues(new Uint8Array(128)));
return randomValues.map(val => {
return chars[val % chars.length];
}).join('');
}
async function createCodeChallenge(codeVerifier) {
const sha = await sha256(codeVerifier);
// https://tools.ietf.org/html/rfc7636#appendix-A
return btoa(sha).split('=')[0].replace(/\+/g, '-').replace(/\//g, '_');
}
const CODE_VERIFIER_STORAGE_KEY = 'decap-cms-pkce-verifier-code';
function createCodeVerifier() {
const codeVerifier = generateVerifierCode();
window.sessionStorage.setItem(CODE_VERIFIER_STORAGE_KEY, codeVerifier);
return codeVerifier;
}
function getCodeVerifier() {
return window.sessionStorage.getItem(CODE_VERIFIER_STORAGE_KEY);
}
function clearCodeVerifier() {
window.sessionStorage.removeItem(CODE_VERIFIER_STORAGE_KEY);
}
class PkceAuthenticator {
constructor(config = {}) {
const baseURL = (0, _trimEnd.default)(config.base_url, '/');
const authEndpoint = (0, _trim.default)(config.auth_endpoint, '/');
const authTokenEndpoint = (0, _trim.default)(config.auth_token_endpoint, '/');
this.auth_url = `${baseURL}/${authEndpoint}`;
this.auth_token_url = `${baseURL}/${authTokenEndpoint}`;
this.auth_token_endpoint_content_type = config.auth_token_endpoint_content_type;
this.appID = config.app_id;
}
async authenticate(options, cb) {
if ((0, _utils.isInsecureProtocol)()) {
return cb(new Error('Cannot authenticate over insecure protocol!'));
}
const authURL = new URL(this.auth_url);
authURL.searchParams.set('client_id', this.appID);
authURL.searchParams.set('redirect_uri', document.location.origin + document.location.pathname);
authURL.searchParams.set('response_type', 'code');
authURL.searchParams.set('scope', options.scope);
const state = JSON.stringify({
auth_type: 'pkce',
nonce: (0, _utils.createNonce)()
});
authURL.searchParams.set('state', state);
authURL.searchParams.set('code_challenge_method', 'S256');
const codeVerifier = createCodeVerifier();
const codeChallenge = await createCodeChallenge(codeVerifier);
authURL.searchParams.set('code_challenge', codeChallenge);
document.location.assign(authURL.href);
}
/**
* Complete authentication if we were redirected back to from the provider.
*/
async completeAuth(cb) {
const params = new URLSearchParams(document.location.search);
// Remove code from url
window.history.replaceState(null, '', document.location.pathname);
if (!params.has('code') && !params.has('error')) {
return;
}
let nonce;
try {
nonce = JSON.parse(params.get('state')).nonce;
} catch (SyntaxError) {
nonce = JSON.parse(params.get('state').replace(/\\"/g, '"')).nonce;
}
const validNonce = (0, _utils.validateNonce)(nonce);
if (!validNonce) {
return cb(new Error('Invalid nonce'));
}
if (params.has('error')) {
return cb(new Error(`${params.get('error')}: ${params.get('error_description')}`));
}
if (params.has('code')) {
const code = params.get('code');
const authURL = new URL(this.auth_token_url);
const token_request_body_object = {
client_id: this.appID,
code,
grant_type: 'authorization_code',
redirect_uri: document.location.origin + document.location.pathname,
code_verifier: getCodeVerifier()
};
const response = await fetch(authURL.href, {
method: 'POST',
body: this.auth_token_endpoint_content_type.startsWith('application/x-www-form-urlencoded') ? new URLSearchParams(Object.entries(token_request_body_object)).toString() : JSON.stringify(token_request_body_object),
headers: {
'Content-Type': this.auth_token_endpoint_content_type
}
});
const data = await response.json();
//no need for verifier code so remove
clearCodeVerifier();
cb(null, _objectSpread({
token: data.access_token
}, data));
}
}
}
exports.default = PkceAuthenticator;

28
node_modules/decap-cms-lib-auth/dist/esm/utils.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createNonce = createNonce;
exports.isInsecureProtocol = isInsecureProtocol;
exports.validateNonce = validateNonce;
var _uuid = require("uuid");
function createNonce() {
const nonce = (0, _uuid.v4)();
window.sessionStorage.setItem('decap-cms-auth', JSON.stringify({
nonce
}));
return nonce;
}
function validateNonce(check) {
const auth = window.sessionStorage.getItem('decap-cms-auth');
const valid = auth && JSON.parse(auth).nonce;
window.localStorage.removeItem('decap-cms-auth');
return check === valid;
}
function isInsecureProtocol() {
return document.location.protocol !== 'https:' &&
// TODO: Is insecure localhost a bad idea as well? I don't think it is, since you are not actually
// sending the token over the internet in this case, assuming the auth URL is secure.
document.location.hostname !== 'localhost' && document.location.hostname !== '127.0.0.1';
}

29
node_modules/decap-cms-lib-auth/package.json generated vendored Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "decap-cms-lib-auth",
"description": "Shared authentication functionality for Decap CMS.",
"version": "3.0.5",
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-auth",
"bugs": "https://github.com/decaporg/decap-cms/issues",
"module": "dist/esm/index.js",
"main": "dist/decap-cms-lib-auth.js",
"license": "MIT",
"files": [
"src/",
"dist/"
],
"keywords": [
"decap-cms"
],
"sideEffects": false,
"scripts": {
"develop": "npm run build:esm -- --watch",
"build": "cross-env NODE_ENV=production webpack",
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
},
"peerDependencies": {
"immutable": "^3.7.6",
"lodash": "^4.17.11",
"uuid": "^8.3.2"
},
"gitHead": "875f4cafa9a82283d28fc5d7871a34550b2a3870"
}

70
node_modules/decap-cms-lib-auth/src/implicit-oauth.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import { Map } from 'immutable';
import trim from 'lodash/trim';
import trimEnd from 'lodash/trimEnd';
import { createNonce, validateNonce, isInsecureProtocol } from './utils';
export default class ImplicitAuthenticator {
constructor(config = {}) {
const baseURL = trimEnd(config.base_url, '/');
const authEndpoint = trim(config.auth_endpoint, '/');
this.auth_url = `${baseURL}/${authEndpoint}`;
this.appID = config.app_id;
this.clearHash = config.clearHash;
}
authenticate(options, cb) {
if (isInsecureProtocol()) {
return cb(new Error('Cannot authenticate over insecure protocol!'));
}
const authURL = new URL(this.auth_url);
authURL.searchParams.set('client_id', this.appID);
authURL.searchParams.set('redirect_uri', document.location.origin + document.location.pathname);
authURL.searchParams.set('response_type', 'token');
authURL.searchParams.set('scope', options.scope);
if (options.prompt != null && options.prompt != undefined) {
authURL.searchParams.set('prompt', options.prompt);
}
if (options.resource != null && options.resource != undefined) {
authURL.searchParams.set('resource', options.resource);
}
const state = JSON.stringify({ auth_type: 'implicit', nonce: createNonce() });
authURL.searchParams.set('state', state);
document.location.assign(authURL.href);
}
/**
* Complete authentication if we were redirected back to from the provider.
*/
completeAuth(cb) {
const hashParams = new URLSearchParams(document.location.hash.replace(/^#?\/?/, ''));
if (!hashParams.has('access_token') && !hashParams.has('error')) {
return;
}
// Remove tokens from hash so that token does not remain in browser history.
this.clearHash();
const params = Map(hashParams.entries());
const { nonce } = JSON.parse(params.get('state'));
const validNonce = validateNonce(nonce);
if (!validNonce) {
return cb(new Error('Invalid nonce'));
}
if (params.has('error')) {
return cb(new Error(`${params.get('error')}: ${params.get('error_description')}`));
}
if (params.has('access_token')) {
const { access_token: token, ...data } = params.toJS();
cb(null, { token, ...data });
}
}
}

5
node_modules/decap-cms-lib-auth/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import NetlifyAuthenticator from './netlify-auth';
import ImplicitAuthenticator from './implicit-oauth';
import PkceAuthenticator from './pkce-oauth';
export const DecapCmsLibAuth = { NetlifyAuthenticator, ImplicitAuthenticator, PkceAuthenticator };
export { NetlifyAuthenticator, ImplicitAuthenticator, PkceAuthenticator };

165
node_modules/decap-cms-lib-auth/src/netlify-auth.js generated vendored Normal file
View File

@@ -0,0 +1,165 @@
import trim from 'lodash/trim';
import trimEnd from 'lodash/trimEnd';
const NETLIFY_API = 'https://api.netlify.com';
const AUTH_ENDPOINT = 'auth';
class NetlifyError {
constructor(err) {
this.err = err;
}
toString() {
return this.err && this.err.message;
}
}
const PROVIDERS = {
github: {
width: 960,
height: 600,
},
gitlab: {
width: 960,
height: 600,
},
bitbucket: {
width: 960,
height: 500,
},
email: {
width: 500,
height: 400,
},
};
class Authenticator {
constructor(config = {}) {
this.site_id = config.site_id || null;
this.base_url = trimEnd(config.base_url, '/') || NETLIFY_API;
this.auth_endpoint = trim(config.auth_endpoint, '/') || AUTH_ENDPOINT;
}
handshakeCallback(options, cb) {
const fn = e => {
if (e.data === 'authorizing:' + options.provider && e.origin === this.base_url) {
window.removeEventListener('message', fn, false);
window.addEventListener('message', this.authorizeCallback(options, cb), false);
return this.authWindow.postMessage(e.data, e.origin);
}
};
return fn;
}
authorizeCallback(options, cb) {
const fn = e => {
if (e.origin !== this.base_url) {
return;
}
if (e.data.indexOf('authorization:' + options.provider + ':success:') === 0) {
const data = JSON.parse(
e.data.match(new RegExp('^authorization:' + options.provider + ':success:(.+)$'))[1],
);
window.removeEventListener('message', fn, false);
this.authWindow.close();
cb(null, data);
}
if (e.data.indexOf('authorization:' + options.provider + ':error:') === 0) {
const err = JSON.parse(
e.data.match(new RegExp('^authorization:' + options.provider + ':error:(.+)$'))[1],
);
window.removeEventListener('message', fn, false);
this.authWindow.close();
cb(new NetlifyError(err));
}
};
return fn;
}
getSiteID() {
if (this.site_id) {
return this.site_id;
}
const host = document.location.host.split(':')[0];
return host === 'localhost' ? 'demo.decapcms.org' : host;
}
authenticate(options, cb) {
const { provider } = options;
const siteID = this.getSiteID();
if (!provider) {
return cb(
new NetlifyError({
message: 'You must specify a provider when calling netlify.authenticate',
}),
);
}
if (!siteID) {
return cb(
new NetlifyError({
message:
"You must set a site_id with netlify.configure({site_id: 'your-site-id'}) to make authentication work from localhost",
}),
);
}
const conf = PROVIDERS[provider] || PROVIDERS.github;
const left = screen.width / 2 - conf.width / 2;
const top = screen.height / 2 - conf.height / 2;
window.addEventListener('message', this.handshakeCallback(options, cb), false);
let url = `${this.base_url}/${this.auth_endpoint}?provider=${options.provider}&site_id=${siteID}`;
if (options.scope) {
url += '&scope=' + options.scope;
}
if (options.login === true) {
url += '&login=true';
}
if (options.beta_invite) {
url += '&beta_invite=' + options.beta_invite;
}
if (options.invite_code) {
url += '&invite_code=' + options.invite_code;
}
this.authWindow = window.open(
url,
'Netlify Authorization',
`width=${conf.width}, height=${conf.height}, top=${top}, left=${left}`,
);
this.authWindow.focus();
}
refresh(options, cb) {
const { provider, refresh_token } = options;
const siteID = this.getSiteID();
const onError = cb || Promise.reject.bind(Promise);
if (!provider || !refresh_token) {
return onError(
new NetlifyError({
message: 'You must specify a provider and refresh token when calling netlify.refresh',
}),
);
}
if (!siteID) {
return onError(
new NetlifyError({
message:
"You must set a site_id with netlify.configure({site_id: 'your-site-id'}) to make token refresh work from localhost",
}),
);
}
const url = `${this.base_url}/${this.auth_endpoint}/refresh?provider=${provider}&site_id=${siteID}&refresh_token=${refresh_token}`;
const refreshPromise = fetch(url, { method: 'POST', body: '' }).then(res => res.json());
// Return a promise if a callback wasn't provided
if (!cb) {
return refreshPromise;
}
// Otherwise, use the provided callback.
refreshPromise.then(data => cb(null, data)).catch(cb);
}
}
export default Authenticator;

140
node_modules/decap-cms-lib-auth/src/pkce-oauth.js generated vendored Normal file
View File

@@ -0,0 +1,140 @@
import trim from 'lodash/trim';
import trimEnd from 'lodash/trimEnd';
import { createNonce, validateNonce, isInsecureProtocol } from './utils';
async function sha256(text) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
const digest = await window.crypto.subtle.digest('SHA-256', data);
const sha = String.fromCharCode(...new Uint8Array(digest));
return sha;
}
// based on https://github.com/auth0/auth0-spa-js/blob/9a83f698127eae7da72691b0d4b1b847567687e3/src/utils.ts#L147
function generateVerifierCode() {
// characters that can be used for codeVerifier
// excludes _~ as if included would cause an uneven distribution as char.length would no longer be a factor of 256
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.';
const randomValues = Array.from(window.crypto.getRandomValues(new Uint8Array(128)));
return randomValues
.map(val => {
return chars[val % chars.length];
})
.join('');
}
async function createCodeChallenge(codeVerifier) {
const sha = await sha256(codeVerifier);
// https://tools.ietf.org/html/rfc7636#appendix-A
return btoa(sha).split('=')[0].replace(/\+/g, '-').replace(/\//g, '_');
}
const CODE_VERIFIER_STORAGE_KEY = 'decap-cms-pkce-verifier-code';
function createCodeVerifier() {
const codeVerifier = generateVerifierCode();
window.sessionStorage.setItem(CODE_VERIFIER_STORAGE_KEY, codeVerifier);
return codeVerifier;
}
function getCodeVerifier() {
return window.sessionStorage.getItem(CODE_VERIFIER_STORAGE_KEY);
}
function clearCodeVerifier() {
window.sessionStorage.removeItem(CODE_VERIFIER_STORAGE_KEY);
}
export default class PkceAuthenticator {
constructor(config = {}) {
const baseURL = trimEnd(config.base_url, '/');
const authEndpoint = trim(config.auth_endpoint, '/');
const authTokenEndpoint = trim(config.auth_token_endpoint, '/');
this.auth_url = `${baseURL}/${authEndpoint}`;
this.auth_token_url = `${baseURL}/${authTokenEndpoint}`;
this.auth_token_endpoint_content_type = config.auth_token_endpoint_content_type;
this.appID = config.app_id;
}
async authenticate(options, cb) {
if (isInsecureProtocol()) {
return cb(new Error('Cannot authenticate over insecure protocol!'));
}
const authURL = new URL(this.auth_url);
authURL.searchParams.set('client_id', this.appID);
authURL.searchParams.set('redirect_uri', document.location.origin + document.location.pathname);
authURL.searchParams.set('response_type', 'code');
authURL.searchParams.set('scope', options.scope);
const state = JSON.stringify({ auth_type: 'pkce', nonce: createNonce() });
authURL.searchParams.set('state', state);
authURL.searchParams.set('code_challenge_method', 'S256');
const codeVerifier = createCodeVerifier();
const codeChallenge = await createCodeChallenge(codeVerifier);
authURL.searchParams.set('code_challenge', codeChallenge);
document.location.assign(authURL.href);
}
/**
* Complete authentication if we were redirected back to from the provider.
*/
async completeAuth(cb) {
const params = new URLSearchParams(document.location.search);
// Remove code from url
window.history.replaceState(null, '', document.location.pathname);
if (!params.has('code') && !params.has('error')) {
return;
}
let nonce;
try {
nonce = JSON.parse(params.get('state')).nonce;
} catch (SyntaxError) {
nonce = JSON.parse(params.get('state').replace(/\\"/g, '"')).nonce;
}
const validNonce = validateNonce(nonce);
if (!validNonce) {
return cb(new Error('Invalid nonce'));
}
if (params.has('error')) {
return cb(new Error(`${params.get('error')}: ${params.get('error_description')}`));
}
if (params.has('code')) {
const code = params.get('code');
const authURL = new URL(this.auth_token_url);
const token_request_body_object = {
client_id: this.appID,
code,
grant_type: 'authorization_code',
redirect_uri: document.location.origin + document.location.pathname,
code_verifier: getCodeVerifier(),
};
const response = await fetch(authURL.href, {
method: 'POST',
body: this.auth_token_endpoint_content_type.startsWith('application/x-www-form-urlencoded')
? new URLSearchParams(Object.entries(token_request_body_object)).toString()
: JSON.stringify(token_request_body_object),
headers: {
'Content-Type': this.auth_token_endpoint_content_type,
},
});
const data = await response.json();
//no need for verifier code so remove
clearCodeVerifier();
cb(null, { token: data.access_token, ...data });
}
}
}

24
node_modules/decap-cms-lib-auth/src/utils.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import { v4 as uuid } from 'uuid';
export function createNonce() {
const nonce = uuid();
window.sessionStorage.setItem('decap-cms-auth', JSON.stringify({ nonce }));
return nonce;
}
export function validateNonce(check) {
const auth = window.sessionStorage.getItem('decap-cms-auth');
const valid = auth && JSON.parse(auth).nonce;
window.localStorage.removeItem('decap-cms-auth');
return check === valid;
}
export function isInsecureProtocol() {
return (
document.location.protocol !== 'https:' &&
// TODO: Is insecure localhost a bad idea as well? I don't think it is, since you are not actually
// sending the token over the internet in this case, assuming the auth URL is secure.
document.location.hostname !== 'localhost' &&
document.location.hostname !== '127.0.0.1'
);
}