UNPKG

wcz-layout

Version:

1 lines 5.96 kB
{"version":3,"file":"providers-Dbl95AxR.mjs","names":["jose","scopes","definedScopes","serverEnv","Scope","ProviderId","Provider","id","label","clientId","clientSecret","issuer","authorizeEndpoint","tokenEndpoint","loginScopes","ReadonlyArray","audience","jwks","ReturnType","createRemoteJWKSet","logoutUrl","returnTo","entraAuthority","ENTRA_TENANT_ID","entra","ENTRA_CLIENT_ID","ENTRA_CLIENT_SECRET","URL","encodeURIComponent","awsResourceScopes","Set","Object","values","Record","map","entry","undefined","aws","filter","scope","buildAws","AWS_ISSUER","AWS_DOMAIN","AWS_CLIENT_ID","AWS_CLIENT_SECRET","providers","getProvider","provider","find","candidate","Error","getProviderByIssuer"],"sources":["../src/lib/auth/providers.ts"],"sourcesContent":["import * as jose from \"jose\";\nimport { scopes as definedScopes } from \"virtual:wcz-layout\";\nimport { serverEnv } from \"~/env\";\nimport type { Scope } from \"~/models/Scopes\";\nimport type { ProviderId } from \"~/models/User\";\n\n/**\n * Identity providers, server-side only. Entra is always present; AWS registers\n * itself when its env vars are set, so an Entra-only app is unchanged and the\n * login page renders a single button.\n */\nexport interface Provider {\n id: ProviderId;\n label: string;\n clientId: string;\n clientSecret: string;\n /** The `iss` claim, used to pick the right provider for an incoming Bearer. */\n issuer: string;\n authorizeEndpoint: string;\n tokenEndpoint: string;\n loginScopes: ReadonlyArray<string>;\n /**\n * Expected `aud` for incoming Bearer JWTs. Undefined for Cognito: its access\n * tokens carry `client_id`, not `aud`, so the user pool itself is the trust\n * boundary — any client in the pool is accepted.\n * ponytail: pool-wide trust; add a client_id allowlist if other pools' clients\n * could ever reach this app.\n */\n audience?: string;\n jwks: ReturnType<typeof jose.createRemoteJWKSet>;\n logoutUrl: (returnTo: string) => string;\n}\n\nconst entraAuthority = `https://login.microsoftonline.com/${serverEnv.ENTRA_TENANT_ID}`;\n\nconst entra: Provider = {\n id: \"entra\",\n label: \"Microsoft\",\n clientId: serverEnv.ENTRA_CLIENT_ID,\n clientSecret: serverEnv.ENTRA_CLIENT_SECRET,\n issuer: `${entraAuthority}/v2.0`,\n authorizeEndpoint: `${entraAuthority}/oauth2/v2.0/authorize`,\n tokenEndpoint: `${entraAuthority}/oauth2/v2.0/token`,\n // `offline_access` yields the multi-resource refresh token every delegated API\n // token is minted from; `User.Read` covers the Graph profile photo.\n loginScopes: [\"openid\", \"profile\", \"offline_access\", \"User.Read\"],\n audience: serverEnv.ENTRA_CLIENT_ID,\n jwks: jose.createRemoteJWKSet(new URL(`${entraAuthority}/discovery/v2.0/keys`)),\n logoutUrl: (returnTo) =>\n `${entraAuthority}/oauth2/v2.0/logout?post_logout_redirect_uri=${encodeURIComponent(returnTo)}`,\n};\n\n/**\n * Cognito cannot widen scopes after login, so every AWS scope the app will ever\n * ask for has to be requested up front. Derived from `scopes.ts` rather than\n * configured separately, so the two can't drift.\n */\nconst awsResourceScopes = [\n ...new Set(\n Object.values(definedScopes as Record<string, Scope>)\n .map((entry) => (typeof entry === \"string\" ? undefined : entry.aws))\n .filter((scope) => scope !== undefined),\n ),\n];\n\nconst buildAws = (): Provider | null => {\n const { AWS_ISSUER, AWS_DOMAIN, AWS_CLIENT_ID, AWS_CLIENT_SECRET } = serverEnv;\n if (!AWS_ISSUER || !AWS_DOMAIN || !AWS_CLIENT_ID || !AWS_CLIENT_SECRET) return null;\n\n return {\n id: \"aws\",\n label: \"AWS\",\n clientId: AWS_CLIENT_ID,\n clientSecret: AWS_CLIENT_SECRET,\n issuer: AWS_ISSUER,\n authorizeEndpoint: `${AWS_DOMAIN}/oauth2/authorize`,\n tokenEndpoint: `${AWS_DOMAIN}/oauth2/token`,\n loginScopes: [\"openid\", \"email\", \"phone\"], // TODO: [\"openid\", \"email\", \"phone\", ...awsResourceScopes]\n jwks: jose.createRemoteJWKSet(new URL(`${AWS_ISSUER}/.well-known/jwks.json`)),\n logoutUrl: (returnTo) =>\n `${AWS_DOMAIN}/logout?client_id=${AWS_CLIENT_ID}&logout_uri=${encodeURIComponent(returnTo)}`,\n };\n};\n\nconst aws = buildAws();\n\nexport const providers: ReadonlyArray<Provider> = aws ? [entra, aws] : [entra];\n\nexport const getProvider = (id: ProviderId): Provider => {\n const provider = providers.find((candidate) => candidate.id === id);\n if (!provider) throw new Error(`Identity provider \"${id}\" is not configured.`);\n return provider;\n};\n\nexport const getProviderByIssuer = (issuer: string | undefined): Provider | undefined =>\n providers.find((candidate) => candidate.issuer === issuer);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAMsB,iBAAiB,qCAAqCnB,UAAUoB;AAEtE,MAAMC,QAAkB;CACtBjB,IAAI;CACJC,OAAO;CACPC,UAAUN,UAAUsB;CACpBf,cAAcP,UAAUuB;CACxBf,QAAQ,GAAGW,eAAc;CACzBV,mBAAmB,GAAGU,eAAc;CACpCT,eAAe,GAAGS,eAAc;CAGhCR,aAAa;EAAC;EAAU;EAAW;EAAkB;CAAW;CAChEE,UAAUb,UAAUsB;CACpBR,MAAMjB,KAAKmB,mBAAmB,IAAIQ,IAAI,GAAGL,eAAc,qBAAsB,CAAC;CAC9EF,YAAYC,aACV,GAAGC,eAAc,+CAAgDM,mBAAmBP,QAAQ;AAChG;AAO0B,CACxB,GAAG,IAAIS,IACLC,OAAOC,OAAO9B,MAAsC,CAAC,CAClDgC,KAAKC,UAAW,OAAOA,UAAU,WAAWC,KAAAA,IAAYD,MAAME,GAAI,CAAC,CACnEC,QAAQC,UAAUA,UAAUH,KAAAA,CAAS,CAC1C,CAAC;AAGH,MAAMI,iBAAkC;CACtC,MAAM,EAAEC,YAAYC,YAAYC,eAAeC,sBAAsBzC;CACrE,IAAI,CAACsC,cAAc,CAACC,cAAc,CAACC,iBAAiB,CAACC,mBAAmB,OAAO;CAE/E,OAAO;EACLrC,IAAI;EACJC,OAAO;EACPC,UAAUkC;EACVjC,cAAckC;EACdjC,QAAQ8B;EACR7B,mBAAmB,GAAG8B,WAAU;EAChC7B,eAAe,GAAG6B,WAAU;EAC5B5B,aAAa;GAAC;GAAU;GAAS;EAAO;EACxCG,MAAMjB,KAAKmB,mBAAmB,IAAIQ,IAAI,GAAGc,WAAU,uBAAwB,CAAC;EAC5ErB,YAAYC,aACV,GAAGqB,WAAU,oBAAqBC,cAAa,cAAef,mBAAmBP,QAAQ;CAC7F;AACF;AAEA,MAAMgB,MAAMG,SAAS;AAErB,MAAaK,YAAqCR,MAAM,CAACb,OAAOa,GAAG,IAAI,CAACb,KAAK;AAE7E,MAAasB,eAAevC,OAA6B;CACvD,MAAMwC,WAAWF,UAAUG,MAAMC,cAAcA,UAAU1C,OAAOA,EAAE;CAClE,IAAI,CAACwC,UAAU,MAAM,IAAIG,MAAM,sBAAsB3C,GAAE,qBAAsB;CAC7E,OAAOwC;AACT;AAEA,MAAaI,uBAAuBxC,WAClCkC,UAAUG,MAAMC,cAAcA,UAAUtC,WAAWA,MAAM"}