{"version":3,"file":"authorize.mjs","names":[],"sources":["../../../src/http/endpoints/authorize.ts"],"sourcesContent":["import type { Router, Response } from 'express'\nimport type { OpenBadgesHttpModule } from '../OpenBadgesHttpModule'\nimport type { ObRequest } from '../router'\n\nimport { sendError } from '../router'\nimport { OpenBadgesAuthCodeRepository } from '../../repository/OpenBadgesAuthCodeRepository'\nimport { OpenBadgesOAuthRepository } from '../../repository/OpenBadgesOAuthRepository'\nimport { isScopeSubset } from '../util/auth'\nimport { OpenBadgesConsentRepository } from '../../repository/OpenBadgesConsentRepository'\n\nfunction urlEncode(params: Record<string, string | number | undefined>) {\n  return Object.entries(params)\n    .filter(([, v]) => v !== undefined)\n    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n    .join('&')\n}\n\nfunction randomCode() {\n  return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)\n}\n\nexport function configureAuthorizeEndpoint(router: Router, module: OpenBadgesHttpModule) {\n  router.get(module.config.authorizePath, async (req: ObRequest, res: Response) => {\n    const { agentContext } = req.requestContext!\n    const { query } = req\n\n    const client_id = String(query.client_id ?? '')\n    const redirect_uri = String(query.redirect_uri ?? '')\n    const response_type = String(query.response_type ?? 'code')\n    const scope = String(query.scope ?? '')\n    const state = String(query.state ?? '')\n    const code_challenge = query.code_challenge ? String(query.code_challenge) : undefined\n    const code_challenge_method = query.code_challenge_method ? String(query.code_challenge_method) : undefined\n\n    if (response_type !== 'code') return sendError(res, 400, 'unsupported_response_type', 'Only code supported')\n    if (!client_id || !redirect_uri) return sendError(res, 400, 'invalid_request', 'client_id and redirect_uri required')\n\n    // Validate client registration, redirect_uri and scope against registration\n    const oauthRepo = agentContext.dependencyManager.resolve(OpenBadgesOAuthRepository)\n    const clientRec = await oauthRepo.findByClientId(agentContext, client_id)\n    if (!clientRec) return sendError(res, 401, 'unauthorized_client', 'Unknown client_id')\n    const reg = (clientRec.clientRegistration as any) || {}\n    const redirectUris: string[] = Array.isArray(reg.redirect_uris) ? reg.redirect_uris : []\n    if (!redirectUris.includes(redirect_uri)) return sendError(res, 400, 'invalid_request', 'redirect_uri not registered')\n    if (!isScopeSubset(scope, reg.scope)) return sendError(res, 400, 'invalid_scope', 'Requested scope not allowed')\n\n    const subject = module.config.defaultSubjectId\n    if (!subject) return sendError(res, 500, 'server_error', 'No defaultSubjectId configured')\n\n    if (module.config.requireConsent) {\n      const consentRepo = agentContext.dependencyManager.resolve(OpenBadgesConsentRepository)\n      const consent = await consentRepo.findByClientAndSubject(agentContext, client_id, subject)\n      if (!consent) {\n        return res\n          .status(403)\n          .json({\n            error: 'consent_required',\n            consent_endpoint: new URL(module.config.consentPath, module.config.baseUrl).toString(),\n            client_id,\n            subject,\n          })\n      }\n    }\n\n    const code = randomCode()\n    const expiresAt = new Date(Date.now() + 60 * 1000)\n    const repo = agentContext.dependencyManager.resolve(OpenBadgesAuthCodeRepository)\n    await repo.save(\n      agentContext,\n      new (require('../../repository/OpenBadgesAuthCodeRecord').OpenBadgesAuthCodeRecord)({\n        code,\n        clientId: client_id,\n        redirectUri: redirect_uri,\n        scope,\n        subject,\n        expiresAt,\n        codeChallenge: code_challenge,\n        codeChallengeMethod: code_challenge_method,\n      })\n    )\n\n    const redirectUrl = `${redirect_uri}${redirect_uri.includes('?') ? '&' : '?'}${urlEncode({ code, state })}`\n    agentContext.config.logger.debug('[OB][OAuth] Issued authorization code', { client_id })\n    res.redirect(302, redirectUrl)\n  })\n}\n"],"mappings":";;;;;;;;;aAIqC;kCAIqD;AAE1F,SAAS,UAAU,QAAqD;AACtE,QAAO,OAAO,QAAQ,OAAO,CAC1B,QAAQ,GAAG,OAAO,MAAM,OAAU,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,mBAAmB,EAAE,CAAC,GAAG,mBAAmB,OAAO,EAAE,CAAC,GAAG,CAC5E,KAAK,IAAI;;AAGd,SAAS,aAAa;AACpB,QAAO,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;AAGlF,SAAgB,2BAA2B,QAAgB,QAA8B;AACvF,QAAO,IAAI,OAAO,OAAO,eAAe,OAAO,KAAgB,QAAkB;EAC/E,MAAM,EAAE,iBAAiB,IAAI;EAC7B,MAAM,EAAE,UAAU;EAElB,MAAM,YAAY,OAAO,MAAM,aAAa,GAAG;EAC/C,MAAM,eAAe,OAAO,MAAM,gBAAgB,GAAG;EACrD,MAAM,gBAAgB,OAAO,MAAM,iBAAiB,OAAO;EAC3D,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG;EACvC,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG;EACvC,MAAM,iBAAiB,MAAM,iBAAiB,OAAO,MAAM,eAAe,GAAG;EAC7E,MAAM,wBAAwB,MAAM,wBAAwB,OAAO,MAAM,sBAAsB,GAAG;AAElG,MAAI,kBAAkB,OAAQ,QAAO,UAAU,KAAK,KAAK,6BAA6B,sBAAsB;AAC5G,MAAI,CAAC,aAAa,CAAC,aAAc,QAAO,UAAU,KAAK,KAAK,mBAAmB,sCAAsC;EAIrH,MAAM,YAAY,MADA,aAAa,kBAAkB,QAAQ,0BAA0B,CACjD,eAAe,cAAc,UAAU;AACzE,MAAI,CAAC,UAAW,QAAO,UAAU,KAAK,KAAK,uBAAuB,oBAAoB;EACtF,MAAM,MAAO,UAAU,sBAA8B,EAAE;AAEvD,MAAI,EAD2B,MAAM,QAAQ,IAAI,cAAc,GAAG,IAAI,gBAAgB,EAAE,EACtE,SAAS,aAAa,CAAE,QAAO,UAAU,KAAK,KAAK,mBAAmB,8BAA8B;AACtH,MAAI,CAAC,cAAc,OAAO,IAAI,MAAM,CAAE,QAAO,UAAU,KAAK,KAAK,iBAAiB,8BAA8B;EAEhH,MAAM,UAAU,OAAO,OAAO;AAC9B,MAAI,CAAC,QAAS,QAAO,UAAU,KAAK,KAAK,gBAAgB,iCAAiC;AAE1F,MAAI,OAAO,OAAO,gBAGhB;OAAI,CADY,MADI,aAAa,kBAAkB,QAAQ,4BAA4B,CACrD,uBAAuB,cAAc,WAAW,QAAQ,CAExF,QAAO,IACJ,OAAO,IAAI,CACX,KAAK;IACJ,OAAO;IACP,kBAAkB,IAAI,IAAI,OAAO,OAAO,aAAa,OAAO,OAAO,QAAQ,CAAC,UAAU;IACtF;IACA;IACD,CAAC;;EAIR,MAAM,OAAO,YAAY;EACzB,MAAM,YAAY,IAAI,KAAK,KAAK,KAAK,GAAG,KAAK,IAAK;AAElD,QADa,aAAa,kBAAkB,QAAQ,6BAA6B,CACtE,KACT,cACA,sFAA0D,yBAA0B;GAClF;GACA,UAAU;GACV,aAAa;GACb;GACA;GACA;GACA,eAAe;GACf,qBAAqB;GACtB,CAAC,CACH;EAED,MAAM,cAAc,GAAG,eAAe,aAAa,SAAS,IAAI,GAAG,MAAM,MAAM,UAAU;GAAE;GAAM;GAAO,CAAC;AACzG,eAAa,OAAO,OAAO,MAAM,yCAAyC,EAAE,WAAW,CAAC;AACxF,MAAI,SAAS,KAAK,YAAY;GAC9B"}