UNPKG

strapi-plugin-sso

Version:
145 lines (122 loc) 5.11 kB
const axios = require("axios"); const {v4} = require('uuid'); const pkceChallenge = require("pkce-challenge").default; const {Buffer} = require('buffer'); const configValidation = () => { const config = strapi.config.get('plugin.strapi-plugin-sso') if (config['OIDC_CLIENT_ID'] && config['OIDC_CLIENT_SECRET'] && config['OIDC_REDIRECT_URI'] && config['OIDC_SCOPES'] && config['OIDC_TOKEN_ENDPOINT'] && config['OIDC_USER_INFO_ENDPOINT'] && config['OIDC_GRANT_TYPE'] && config['OIDC_FAMILY_NAME_FIELD'] && config['OIDC_GIVEN_NAME_FIELD'] && config['OIDC_AUTHORIZATION_ENDPOINT'] ) { return config } throw new Error('OIDC_AUTHORIZATION_ENDPOINT,OIDC_TOKEN_ENDPOINT, OIDC_USER_INFO_ENDPOINT,OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI, and OIDC_SCOPES are required') } const oidcSignIn = async (ctx) => { let { state } = ctx.query; const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPES, OIDC_AUTHORIZATION_ENDPOINT } = configValidation(); // Generate code verifier and code challenge const { code_verifier: codeVerifier, code_challenge: codeChallenge } = pkceChallenge(); // Store the code verifier in the session ctx.session.codeVerifier = codeVerifier; if (!state) { state = crypto.getRandomValues(Buffer.alloc(32)).toString('base64url'); } ctx.session.oidcState = state; const params = new URLSearchParams(); params.append('response_type', 'code'); params.append('client_id', OIDC_CLIENT_ID); params.append('redirect_uri', OIDC_REDIRECT_URI); params.append('scope', OIDC_SCOPES); params.append('code_challenge', codeChallenge); params.append('code_challenge_method', 'S256'); params.append('state', state); const authorizationUrl = `${OIDC_AUTHORIZATION_ENDPOINT}?${params.toString()}`; ctx.set('Location', authorizationUrl); return ctx.send({}, 302); }; const oidcSignInCallback = async (ctx) => { const config = configValidation() const httpClient = axios.create() const userService = strapi.service('admin::user') const tokenService = strapi.service('admin::token') const oauthService = strapi.plugin('strapi-plugin-sso').service('oauth') const roleService = strapi.plugin('strapi-plugin-sso').service('role') if (!ctx.query.code) { return ctx.send(oauthService.renderSignUpError(`code Not Found`)) } if (!ctx.query.state || ctx.query.state !== ctx.session.oidcState) { return ctx.send(oauthService.renderSignUpError(`Invalid state`)) } const params = new URLSearchParams(); params.append('code', ctx.query.code); params.append('client_id', config['OIDC_CLIENT_ID']); params.append('client_secret', config['OIDC_CLIENT_SECRET']); params.append('redirect_uri', config['OIDC_REDIRECT_URI']); params.append('grant_type', config['OIDC_GRANT_TYPE']); // Include the code verifier from the session params.append("code_verifier", ctx.session.codeVerifier); try { const response = await httpClient.post(config['OIDC_TOKEN_ENDPOINT'], params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }) let userInfoEndpointHeaders = {}; let userInfoEndpointParameters = `?access_token=${response.data.access_token}`; if (config["OIDC_USER_INFO_ENDPOINT_WITH_AUTH_HEADER"]) { userInfoEndpointHeaders = { headers: { Authorization: `Bearer ${response.data.access_token}` }, }; userInfoEndpointParameters = ""; } const userInfoEndpoint = `${config["OIDC_USER_INFO_ENDPOINT"]}${userInfoEndpointParameters}`; const userResponse = await httpClient.get( userInfoEndpoint, userInfoEndpointHeaders ); const email = userResponse.data.email const dbUser = await userService.findOneByEmail(email) let activateUser; let jwtToken; if (dbUser) { // Already registered activateUser = dbUser; jwtToken = await tokenService.createJwtToken(dbUser) } else { // Register a new account const oidcRoles = await roleService.oidcRoles() const roles = oidcRoles && oidcRoles['roles'] ? oidcRoles['roles'].map(role => ({ id: role })) : [] const defaultLocale = oauthService.localeFindByHeader(ctx.request.headers) activateUser = await oauthService.createUser( email, userResponse.data[config['OIDC_FAMILY_NAME_FIELD']], userResponse.data[config['OIDC_GIVEN_NAME_FIELD']], defaultLocale, roles, ) jwtToken = await tokenService.createJwtToken(activateUser) // Trigger webhook await oauthService.triggerWebHook(activateUser) } // Login Event Call oauthService.triggerSignInSuccess(activateUser) // Client-side authentication persistence and redirection const nonce = v4() const html = oauthService.renderSignUpSuccess(jwtToken, activateUser, nonce) ctx.set('Content-Security-Policy', `script-src 'nonce-${nonce}'`) ctx.send(html); } catch (e) { console.error(e) ctx.send(oauthService.renderSignUpError(e.message)) } }; module.exports = { oidcSignIn, oidcSignInCallback, };