UNPKG

@sphereon/ssi-sdk.siopv2-oid4vp-rp-rest-api

Version:

184 lines (172 loc) • 8.2 kB
import { AuthorizationRequestState, AuthorizationResponseStateStatus } from '@sphereon/did-auth-siop' import { checkAuth, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-express-support' import { AuthStatusResponse, GenerateAuthRequestURIResponse, uriWithBase } from '@sphereon/ssi-sdk.siopv2-oid4vp-common' import { AuthorizationResponseStateWithVerifiedData, VerifiedDataMode } from '@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth' import { Request, Response, Router } from 'express' import uuid from 'short-uuid' import { ICreateAuthRequestWebappEndpointOpts, IRequiredContext } from './types' import { shaHasher as defaultHasher } from '@sphereon/ssi-sdk.core' export function createAuthRequestWebappEndpoint(router: Router, context: IRequiredContext, opts?: ICreateAuthRequestWebappEndpointOpts) { if (opts?.enabled === false) { console.log(`createAuthRequest Webapp endpoint is disabled`) return } const path = opts?.path ?? '/webapp/definitions/:definitionId/auth-requests' router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { try { // if (!request.agent) throw Error('No agent configured') const definitionId = request.params.definitionId if (!definitionId) { return sendErrorResponse(response, 400, 'No definitionId query parameter provided') } const state: string = request.body.state ?? uuid.uuid() const correlationId = request.body.correlationId ?? state const qrCodeOpts = request.body.qrCodeOpts ?? opts?.qrCodeOpts const requestByReferenceURI = uriWithBase(`/siop/definitions/${definitionId}/auth-requests/${state}`, { baseURI: opts?.siopBaseURI, }) const responseURI = uriWithBase(`/siop/definitions/${definitionId}/auth-responses/${state}`, { baseURI: opts?.siopBaseURI }) // first version is for backwards compat const responseRedirectURI = ('response_redirect_uri' in request.body && (request.body.response_redirect_uri as string | undefined)) ?? ('responseRedirectURI' in request.body && (request.body.responseRedirectURI as string | undefined)) const authRequestURI = await context.agent.siopCreateAuthRequestURI({ definitionId, correlationId, state, nonce: uuid.uuid(), requestByReferenceURI, responseURIType: 'response_uri', responseURI, ...(responseRedirectURI && { responseRedirectURI }), }) let qrCodeDataUri: string | undefined if (qrCodeOpts) { const { AwesomeQR } = await import('awesome-qr') const qrCode = new AwesomeQR({ ...qrCodeOpts, text: authRequestURI }) qrCodeDataUri = `data:image/png;base64,${(await qrCode.draw())!.toString('base64')}` } const authRequestBody: GenerateAuthRequestURIResponse = { correlationId, state, definitionId, authRequestURI, authStatusURI: `${uriWithBase(opts?.webappAuthStatusPath ?? '/webapp/auth-status', { baseURI: opts?.webappBaseURI })}`, ...(qrCodeDataUri && { qrCodeDataUri }), } console.log(`Auth Request URI data to send back: ${JSON.stringify(authRequestBody)}`) return response.json(authRequestBody) } catch (error) { return sendErrorResponse(response, 500, 'Could not create an authorization request URI', error) } }) } export function authStatusWebappEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) { if (opts?.enabled === false) { console.log(`authStatus Webapp endpoint is disabled`) return } const path = opts?.path ?? '/webapp/auth-status' router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { try { console.log('Received auth-status request...') const correlationId: string = request.body.correlationId as string const definitionId: string = request.body.definitionId as string const requestState = correlationId && definitionId ? await context.agent.siopGetAuthRequestState({ correlationId, definitionId, errorOnNotFound: false, }) : undefined if (!requestState || !definitionId || !correlationId) { console.log( `No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}`, ) response.statusCode = 404 const statusBody: AuthStatusResponse = { status: requestState ? requestState.status : 'error', error: 'No authentication request mapping could be found for the given URL.', correlationId, definitionId, lastUpdated: requestState ? requestState.lastUpdated : Date.now(), } return response.json(statusBody) } let includeVerifiedData: VerifiedDataMode = VerifiedDataMode.NONE if ('includeVerifiedData' in request.body) { includeVerifiedData = request.body.includeVerifiedData as VerifiedDataMode } let responseState if (requestState.status === 'sent') { responseState = (await context.agent.siopGetAuthResponseState({ correlationId, definitionId, includeVerifiedData: includeVerifiedData, errorOnNotFound: false, })) as AuthorizationResponseStateWithVerifiedData } const overallState: AuthorizationRequestState | AuthorizationResponseStateWithVerifiedData = responseState ?? requestState const statusBody: AuthStatusResponse = { status: overallState.status, ...(overallState.error ? { error: overallState.error?.message } : {}), correlationId, definitionId, lastUpdated: overallState.lastUpdated, ...(responseState && responseState.status === AuthorizationResponseStateStatus.VERIFIED ? { payload: await responseState.response.mergedPayloads({ hasher: defaultHasher }), verifiedData: responseState.verifiedData, } : {}), } console.debug(`Will send auth status: ${JSON.stringify(statusBody)}`) if (overallState.status === 'error') { response.statusCode = 500 return response.json(statusBody) } response.statusCode = 200 return response.json(statusBody) } catch (error) { return sendErrorResponse(response, 500, error.message, error) } }) } export function removeAuthRequestStateWebappEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) { if (opts?.enabled === false) { console.log(`removeAuthStatus Webapp endpoint is disabled`) return } const path = opts?.path ?? '/webapp/definitions/:definitionId/auth-requests/:correlationId' router.delete(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { try { const correlationId: string = request.params.correlationId const definitionId: string = request.params.definitionId if (!correlationId || !definitionId) { console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) return sendErrorResponse(response, 404, 'No authorization request could be found') } response.statusCode = 200 return response.json(await context.agent.siopDeleteAuthState({ definitionId, correlationId })) } catch (error) { return sendErrorResponse(response, 500, error.message, error) } }) } export function getDefinitionsEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) { if (opts?.enabled === false) { console.log(`getDefinitions Webapp endpoint is disabled`) return } const path = opts?.path ?? '/webapp/definitions' router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { try { const definitions = await context.agent.pdmGetDefinitions() response.statusCode = 200 return response.json(definitions) } catch (error) { return sendErrorResponse(response, 500, error.message, error) } }) }