decap-cms-ui-auth
Version:
UI auth library for Decap CMS
146 lines (145 loc) • 11.4 kB
JavaScript
import _styled from "@emotion/styled/base";
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return "You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."; }
import React from 'react';
import PropTypes from 'prop-types';
import jwtDecode from 'jwt-decode';
import { PkceAuthenticator } from 'decap-cms-lib-auth';
import { AuthenticationPage, Icon } from 'decap-cms-ui-default';
import { jsx as ___EmotionJSX } from "@emotion/react";
const LoginButtonIcon = /*#__PURE__*/_styled(Icon, {
target: "ent5iu90",
label: "LoginButtonIcon"
})(process.env.NODE_ENV === "production" ? {
name: "1gnqu05",
styles: "margin-right:18px"
} : {
name: "1gnqu05",
styles: "margin-right:18px/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9QS0NFQXV0aGVudGljYXRpb25QYWdlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU9vQyIsImZpbGUiOiIuLi8uLi9zcmMvUEtDRUF1dGhlbnRpY2F0aW9uUGFnZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCc7XG5pbXBvcnQgUHJvcFR5cGVzIGZyb20gJ3Byb3AtdHlwZXMnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IGp3dERlY29kZSBmcm9tICdqd3QtZGVjb2RlJztcbmltcG9ydCB7IFBrY2VBdXRoZW50aWNhdG9yIH0gZnJvbSAnZGVjYXAtY21zLWxpYi1hdXRoJztcbmltcG9ydCB7IEF1dGhlbnRpY2F0aW9uUGFnZSwgSWNvbiB9IGZyb20gJ2RlY2FwLWNtcy11aS1kZWZhdWx0JztcblxuY29uc3QgTG9naW5CdXR0b25JY29uID0gc3R5bGVkKEljb24pYFxuICBtYXJnaW4tcmlnaHQ6IDE4cHg7XG5gO1xuXG5mdW5jdGlvbiBub3JtYWxpemVDbGFpbXNUb1VzZXIoXG4gIGVtYWlsX2NsYWltLFxuICBmdWxsX25hbWVfY2xhaW0sXG4gIGZpcnN0X25hbWVfY2xhaW0sXG4gIGxhc3RfbmFtZV9jbGFpbSxcbiAgYXZhdGFyX3VybF9jbGFpbSxcbikge1xuICByZXR1cm4gKHVzZXIsIGNsYWltcykgPT4ge1xuICAgIGlmICghY2xhaW1zKSByZXR1cm47XG5cbiAgICBpZiAoIXVzZXIuZW1haWwgJiYgY2xhaW1zW2VtYWlsX2NsYWltXSkge1xuICAgICAgdXNlci5lbWFpbCA9IGNsYWltc1tlbWFpbF9jbGFpbV07XG4gICAgfVxuICAgIGlmICghdXNlci51c2VyX21ldGFkYXRhLmZ1bGxfbmFtZSAmJiBmdWxsX25hbWVfY2xhaW0gJiYgY2xhaW1zW2Z1bGxfbmFtZV9jbGFpbV0pIHtcbiAgICAgIHVzZXIudXNlcl9tZXRhZGF0YS5mdWxsX25hbWUgPSBjbGFpbXNbZnVsbF9uYW1lX2NsYWltXTtcbiAgICB9XG4gICAgaWYgKCF1c2VyLnVzZXJfbWV0YWRhdGEuZnVsbF9uYW1lICYmIChmaXJzdF9uYW1lX2NsYWltIHx8IGxhc3RfbmFtZV9jbGFpbSkpIHtcbiAgICAgIGNvbnN0IG5hbWUgPSBbXTtcbiAgICAgIGlmIChjbGFpbXNbZmlyc3RfbmFtZV9jbGFpbV0pIG5hbWUucHVzaChjbGFpbXNbZmlyc3RfbmFtZV9jbGFpbV0pO1xuICAgICAgaWYgKGNsYWltc1tsYXN0X25hbWVfY2xhaW1dKSBuYW1lLnB1c2goY2xhaW1zW2xhc3RfbmFtZV9jbGFpbV0pO1xuICAgICAgaWYgKG5hbWUubGVuZ3RoKSB7XG4gICAgICAgIHVzZXIudXNlcl9tZXRhZGF0YS5mdWxsX25hbWUgPSBuYW1lLmpvaW4oJyAnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCF1c2VyLnVzZXJfbWV0YWRhdGEuYXZhdGFyX3VybCAmJiBhdmF0YXJfdXJsX2NsYWltICYmIGNsYWltc1thdmF0YXJfdXJsX2NsYWltXSkge1xuICAgICAgdXNlci51c2VyX21ldGFkYXRhLmF2YXRhcl91cmwgPSBjbGFpbXNbYXZhdGFyX3VybF9jbGFpbV07XG4gICAgfVxuICB9O1xufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQS0NFQXV0aGVudGljYXRpb25QYWdlIGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHtcbiAgc3RhdGljIHByb3BUeXBlcyA9IHtcbiAgICBpblByb2dyZXNzOiBQcm9wVHlwZXMuYm9vbCxcbiAgICBjb25maWc6IFByb3BUeXBlcy5vYmplY3QuaXNSZXF1aXJlZCxcbiAgICBvbkxvZ2luOiBQcm9wVHlwZXMuZnVuYy5pc1JlcXVpcmVkLFxuICAgIHQ6IFByb3BUeXBlcy5mdW5jLmlzUmVxdWlyZWQsXG4gIH07XG5cbiAgc3RhdGUgPSB7fTtcblxuICBjb21wb25lbnREaWRNb3VudCgpIHtcbiAgICAvLyBPbGQgY29uZmlndXJhdGlvbiBvcHRpb25zLCBhdmFpbGFibGUgZnJvbSB0aGUgYmFja2VuZCBjb25maWd1cmF0aW9uXG4gICAgY29uc3Qge1xuICAgICAgYmFzZV91cmw6IGJhY2tlbmRfYmFzZV91cmwgPSAnJyxcbiAgICAgIGFwcF9pZDogYmFja2VuZF9hcHBfaWQgPSAnJyxcbiAgICAgIGF1dGhfZW5kcG9pbnQ6IGJhY2tlbmRfYXV0aF9lbmRwb2ludCA9ICdvYXV0aDIvYXV0aG9yaXplJyxcbiAgICAgIGF1dGhfdG9rZW5fZW5kcG9pbnQ6IGJhY2tlbmRfYXV0aF90b2tlbl9lbmRwb2ludCA9ICdvYXV0aDIvdG9rZW4nLFxuICAgIH0gPSB0aGlzLnByb3BzLmNvbmZpZy5iYWNrZW5kO1xuICAgIC8vIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMsIHNlcGFyYXRlbHkgZGVmaW5lZCBpbiB0aGUgXCJhdXRoXCIgY29uZmlndXJhdGlvblxuICAgIGNvbnN0IHtcbiAgICAgIHVzZV9vaWRjID0gZmFsc2UsXG4gICAgICBiYXNlX3VybCA9IGJhY2tlbmRfYmFzZV91cmwsXG4gICAgICBhdXRoX2VuZHBvaW50ID0gYmFja2VuZF9hdXRoX2VuZHBvaW50LFxuICAgICAgYXV0aF90b2tlbl9lbmRwb2ludCA9IGJhY2tlbmRfYXV0aF90b2tlbl9lbmRwb2ludCxcbiAgICAgIGFwcF9pZCA9IGJhY2tlbmRfYXBwX2lkLFxuICAgICAgYXV0aF90b2tlbl9lbmRwb2ludF9jb250ZW50X3R5cGUgPSAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkOyBjaGFyc2V0PXV0Zi04JyxcbiAgICAgIGVtYWlsX2NsYWltID0gJ2VtYWlsJyxcbiAgICAgIGZ1bGxfbmFtZV9jbGFpbSxcbiAgICAgIGZpcnN0X25hbWVfY2xhaW0sXG4gICAgICBsYXN0X25hbWVfY2xhaW0sXG4gICAgICBhdmF0YXJfdXJsX2NsYWltLFxuICAgIH0gPSB0aGlzLnByb3BzLmNvbmZpZy5hdXRoIHx8IHt9O1xuXG4gICAgY29uc3Qgbm9ybWFsaXplQ2xhaW1zID0gbm9ybWFsaXplQ2xhaW1zVG9Vc2VyKFxuICAgICAgZW1haWxfY2xhaW0sXG4gICAgICBmdWxsX25hbWVfY2xhaW0sXG4gICAgICBmaXJzdF9uYW1lX2NsYWltLFxuICAgICAgbGFzdF9uYW1lX2NsYWltLFxuICAgICAgYXZhdGFyX3VybF9jbGFpbSxcbiAgICApO1xuXG4gICAgdGhpcy5hdXRoID0gbmV3IFBrY2VBdXRoZW50aWNhdG9yKHtcbiAgICAgIGJhc2VfdXJsLFxuICAgICAgYXBwX2lkLFxuICAgICAgdXNlX29pZGMsXG4gICAgICBhdXRoX2VuZHBvaW50LFxuICAgICAgYXV0aF90b2tlbl9lbmRwb2ludCxcbiAgICAgIGF1dGhfdG9rZW5fZW5kcG9pbnRfY29udGVudF90eXBlLFxuICAgIH0pO1xuXG4gICAgLy8gQ29tcGxldGUgYXV0aGVudGljYXRpb24gaWYgd2Ugd2VyZSByZWRpcmVjdGVkIGJhY2sgZnJvbSB0aGUgcHJvdmlkZXIuXG4gICAgdGhpcy5hdXRoLmNvbXBsZXRlQXV0aCgoZXJyLCBkYXRhKSA9PiB7XG4gICAgICBpZiAoZXJyKSB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyBsb2dpbkVycm9yOiBlcnIudG9TdHJpbmcoKSB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBkYXRhLnVzZXJfbWV0YWRhdGEgPSB7fTtcbiAgICAgIGlmIChkYXRhLmFjY2Vzc190b2tlbikge1xuICAgICAgICBkYXRhLnRva2VuID0gZGF0YS5hY2Nlc3NfdG9rZW47XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgZGF0YS5jbGFpbXMgPSBqd3REZWNvZGUoZGF0YS5hY2Nlc3NfdG9rZW4pO1xuICAgICAgICAgIG5vcm1hbGl6ZUNsYWltcyhkYXRhLCBkYXRhLmNsYWltcyk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8qIElnbm9yZSAqL1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoZGF0YS5pZF90b2tlbikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGRhdGEuaWRDbGFpbXMgPSBqd3REZWNvZGUoZGF0YS5pZF90b2tlbik7XG4gICAgICAgICAgbm9ybWFsaXplQ2xhaW1zKGRhdGEsIGRhdGEuaWRDbGFpbXMpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvKiBJZ25vcmUgKi9cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0aGlzLnByb3BzLm9uTG9naW4oZGF0YSk7XG4gICAgfSk7XG4gIH1cblxuICBoYW5kbGVMb2dpbiA9IGUgPT4ge1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICBjb25zdCBzY29wZSA9IHRoaXMucHJvcHMuY29uZmlnLmF1dGg/LnNjb3BlIHx8IHRoaXMucHJvcHMuY29uZmlnLmF1dGhfc2NvcGUgfHwgJ29wZW5pZCBlbWFpbCc7XG4gICAgdGhpcy5hdXRoLmF1dGhlbnRpY2F0ZSh7IHNjb3BlIH0sIChlcnIsIGRhdGEpID0+IHtcbiAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IGxvZ2luRXJyb3I6IGVyci50b1N0cmluZygpIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnByb3BzLm9uTG9naW4oZGF0YSk7XG4gICAgfSk7XG4gIH07XG5cbiAgcmVuZGVyKCkge1xuICAgIGNvbnN0IHsgaW5Qcm9ncmVzcywgY29uZmlnLCB0IH0gPSB0aGlzLnByb3BzO1xuICAgIHJldHVybiAoXG4gICAgICA8QXV0aGVudGljYXRpb25QYWdlXG4gICAgICAgIG9uTG9naW49e3RoaXMuaGFuZGxlTG9naW59XG4gICAgICAgIGxvZ2luRGlzYWJsZWQ9e2luUHJvZ3Jlc3N9XG4gICAgICAgIGxvZ2luRXJyb3JNZXNzYWdlPXt0aGlzLnN0YXRlLmxvZ2luRXJyb3J9XG4gICAgICAgIGxvZ29Vcmw9e2NvbmZpZy5sb2dvX3VybH0gLy8gRGVwcmVjYXRlZCwgcmVwbGFjZWQgYnkgYGxvZ28uc3JjYFxuICAgICAgICBsb2dvPXtjb25maWcubG9nb31cbiAgICAgICAgc2l0ZVVybD17Y29uZmlnLnNpdGVfdXJsfVxuICAgICAgICByZW5kZXJCdXR0b25Db250ZW50PXsoKSA9PiAoXG4gICAgICAgICAgPFJlYWN0LkZyYWdtZW50PlxuICAgICAgICAgICAgPExvZ2luQnV0dG9uSWNvbiB0eXBlPVwibGlua1wiIC8+IHtpblByb2dyZXNzID8gdCgnYXV0aC5sb2dnaW5nSW4nKSA6IHQoJ2F1dGgubG9naW4nKX1cbiAgICAgICAgICA8L1JlYWN0LkZyYWdtZW50PlxuICAgICAgICApfVxuICAgICAgICB0PXt0fVxuICAgICAgLz5cbiAgICApO1xuICB9XG59XG4iXX0= */",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
});
function normalizeClaimsToUser(email_claim, full_name_claim, first_name_claim, last_name_claim, avatar_url_claim) {
return (user, claims) => {
if (!claims) return;
if (!user.email && claims[email_claim]) {
user.email = claims[email_claim];
}
if (!user.user_metadata.full_name && full_name_claim && claims[full_name_claim]) {
user.user_metadata.full_name = claims[full_name_claim];
}
if (!user.user_metadata.full_name && (first_name_claim || last_name_claim)) {
const name = [];
if (claims[first_name_claim]) name.push(claims[first_name_claim]);
if (claims[last_name_claim]) name.push(claims[last_name_claim]);
if (name.length) {
user.user_metadata.full_name = name.join(' ');
}
}
if (!user.user_metadata.avatar_url && avatar_url_claim && claims[avatar_url_claim]) {
user.user_metadata.avatar_url = claims[avatar_url_claim];
}
};
}
export default class PKCEAuthenticationPage extends React.Component {
static propTypes = {
inProgress: PropTypes.bool,
config: PropTypes.object.isRequired,
onLogin: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
state = {};
componentDidMount() {
// Old configuration options, available from the backend configuration
const {
base_url: backend_base_url = '',
app_id: backend_app_id = '',
auth_endpoint: backend_auth_endpoint = 'oauth2/authorize',
auth_token_endpoint: backend_auth_token_endpoint = 'oauth2/token'
} = this.props.config.backend;
// New configuration options, separately defined in the "auth" configuration
const {
use_oidc = false,
base_url = backend_base_url,
auth_endpoint = backend_auth_endpoint,
auth_token_endpoint = backend_auth_token_endpoint,
app_id = backend_app_id,
auth_token_endpoint_content_type = 'application/x-www-form-urlencoded; charset=utf-8',
email_claim = 'email',
full_name_claim,
first_name_claim,
last_name_claim,
avatar_url_claim
} = this.props.config.auth || {};
const normalizeClaims = normalizeClaimsToUser(email_claim, full_name_claim, first_name_claim, last_name_claim, avatar_url_claim);
this.auth = new PkceAuthenticator({
base_url,
app_id,
use_oidc,
auth_endpoint,
auth_token_endpoint,
auth_token_endpoint_content_type
});
// Complete authentication if we were redirected back from the provider.
this.auth.completeAuth((err, data) => {
if (err) {
this.setState({
loginError: err.toString()
});
return;
}
data.user_metadata = {};
if (data.access_token) {
data.token = data.access_token;
try {
data.claims = jwtDecode(data.access_token);
normalizeClaims(data, data.claims);
} catch {
/* Ignore */
}
}
if (data.id_token) {
try {
data.idClaims = jwtDecode(data.id_token);
normalizeClaims(data, data.idClaims);
} catch {
/* Ignore */
}
}
this.props.onLogin(data);
});
}
handleLogin = e => {
e.preventDefault();
const scope = this.props.config.auth?.scope || this.props.config.auth_scope || 'openid email';
this.auth.authenticate({
scope
}, (err, data) => {
if (err) {
this.setState({
loginError: err.toString()
});
return;
}
this.props.onLogin(data);
});
};
render() {
const {
inProgress,
config,
t
} = this.props;
return ___EmotionJSX(AuthenticationPage, {
onLogin: this.handleLogin,
loginDisabled: inProgress,
loginErrorMessage: this.state.loginError,
logoUrl: config.logo_url // Deprecated, replaced by `logo.src`
,
logo: config.logo,
siteUrl: config.site_url,
renderButtonContent: () => ___EmotionJSX(React.Fragment, null, ___EmotionJSX(LoginButtonIcon, {
type: "link"
}), " ", inProgress ? t('auth.loggingIn') : t('auth.login')),
t: t
});
}
}