UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

104 lines (87 loc) 3.58 kB
const cds = require('../../../index.js') const LOG = cds.log('auth') const { createSecurityContext, Token, XsuaaService, XsaService, errors: { ValidationError } } = require('./xssec') module.exports = function jwt_auth(config) { const { kind, credentials, config: serviceConfig = {} } = config if (!credentials) throw new Error( `Authentication kind "${kind}" configured, but no XSUAA instance bound to application. ` + 'Either bind an XSUAA instance, or switch to an authentication kind that does not require a binding.' ) // enable signature cache by default serviceConfig.validation ??= {} if (!('signatureCache' in serviceConfig.validation)) serviceConfig.validation.signatureCache = { enabled: true } // activate decode cache if not already done or explicitely disabled by setting Token.decodeCache to false or undefined if (Token.decodeCache === null) Token.enableDecodeCache() const auth_service = !credentials.uaadomain ? new XsaService(credentials, serviceConfig) : new XsuaaService(credentials, serviceConfig) const user_factory = get_user_factory(credentials, credentials.xsappname, kind) return async function jwt_auth(req, _, next) { if (!req.headers.authorization) return next() try { const securityContext = await createSecurityContext(auth_service, { req }) const ctx = cds.context ctx.user = user_factory(securityContext) ctx.tenant = securityContext.token.getZoneId() // REVISIT: remove compat in cds^10 Object.defineProperty(req, 'authInfo', { get() { cds.utils.deprecated({ kind: 'API', old: 'cds.context.http.req.authInfo', use: 'cds.context.user.authInfo' }) return securityContext } }) } catch (e) { if (e instanceof ValidationError) { if (e.token?.payload) e.token_payload = e.token.payload LOG.warn('Unauthenticated request:', e) return next(401) } LOG.error('Error while authenticating user:', e) return next(500) } next() } } function get_user_factory(credentials, xsappname, kind) { xsappname = xsappname + '.' return function user_factory(securityContext) { const tokenInfo = securityContext.token const payload = tokenInfo.getPayload() let id = payload.user_name const roles = {} for (let scope of payload.scope) { let role = scope.replace(xsappname, '') // Roles = scope names w/o xsappname... if (role in { 'internal-user': 1, 'system-user': 1 }) continue // Disallow setting system roles from external else roles[role] = 1 } // Add system roles in case of client credentials flow if (payload.grant_type in { client_credentials: 1, client_x509: 1 }) { id = 'system' roles['system-user'] = 1 if (tokenInfo.getClientId() === credentials.clientid) roles['internal-user'] = 1 } const attr = { ...payload['xs.user.attributes'] } if (kind === 'xsuaa') { attr.logonName = payload.user_name attr.givenName = payload.given_name attr.familyName = payload.family_name attr.email = payload.email } const user = new cds.User({ id, roles, attr, authInfo: securityContext }) // REVISIT: remove compat in cds^10 Object.defineProperty(user, 'tokenInfo', { get() { cds.utils.deprecated({ kind: 'API', old: 'cds.context.user.tokenInfo', use: 'cds.context.user.authInfo.token' }) return securityContext.token } }) return user } } module.exports._get_user_factory = get_user_factory