From 7a01cc3edc01269d4d84df87e78e955ad5936a19 Mon Sep 17 00:00:00 2001 From: Gage Keenan <6761186+kilakewe@users.noreply.github.com> Date: Sat, 14 Dec 2024 00:08:43 +1300 Subject: [PATCH] feat(cognito): integrate OpenID Connect discovery for improved OAuth flow * feat(cognito): integrate OpenID Connect discovery for improved OAuth flow * feat(cognito): enhance OAuth flow by including client secret in discovery process * chore: update lockfile --- package.json | 1 + pnpm-lock.yaml | 21 +++++++++++++++ src/runtime/server/lib/oauth/cognito.ts | 34 ++++++++++++------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 404c714..13b3312 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "hookable": "^5.5.3", "ofetch": "^1.4.1", "ohash": "^1.1.4", + "openid-client": "^6.1.4", "pathe": "^1.1.2", "scule": "^1.3.0", "uncrypto": "^0.1.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c31ca1..6de4aaf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: ohash: specifier: ^1.1.4 version: 1.1.4 + openid-client: + specifier: ^6.1.4 + version: 6.1.7 pathe: specifier: ^1.1.2 version: 1.1.2 @@ -3350,6 +3353,9 @@ packages: resolution: {integrity: sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==} hasBin: true + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} @@ -3822,6 +3828,9 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + oauth4webapi@3.1.4: + resolution: {integrity: sha512-eVfN3nZNbok2s/ROifO0UAc5G8nRoLSbrcKJ09OqmucgnhXEfdIQOR4gq1eJH1rN3gV7rNw62bDEgftsgFtBEg==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3868,6 +3877,9 @@ packages: peerDependencies: typescript: ^5.x + openid-client@6.1.7: + resolution: {integrity: sha512-JfY/KvQgOutmG2P+oVNKInE7zIh+im1MQOaO7g5CtNnTWMociA563WweiEMKfR9ry9XG3K2HGvj9wEqhCQkPMg==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -9252,6 +9264,8 @@ snapshots: jiti@2.4.0: {} + jose@5.9.6: {} + js-levenshtein@1.1.6: {} js-tokens@4.0.0: {} @@ -10018,6 +10032,8 @@ snapshots: pkg-types: 1.2.1 ufo: 1.5.4 + oauth4webapi@3.1.4: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -10074,6 +10090,11 @@ snapshots: transitivePeerDependencies: - encoding + openid-client@6.1.7: + dependencies: + jose: 5.9.6 + oauth4webapi: 3.1.4 + optionator@0.9.4: dependencies: deep-is: 0.1.4 diff --git a/src/runtime/server/lib/oauth/cognito.ts b/src/runtime/server/lib/oauth/cognito.ts index ce0d9a2..3e13371 100644 --- a/src/runtime/server/lib/oauth/cognito.ts +++ b/src/runtime/server/lib/oauth/cognito.ts @@ -1,10 +1,11 @@ +import type { OAuthConfig } from '#auth-utils' +import { useRuntimeConfig } from '#imports' +import { defu } from 'defu' import type { H3Event } from 'h3' import { eventHandler, getQuery, sendRedirect } from 'h3' +import { discovery } from 'openid-client' import { withQuery } from 'ufo' -import { defu } from 'defu' -import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from '../utils' -import { useRuntimeConfig } from '#imports' -import type { OAuthConfig } from '#auth-utils' +import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleMissingConfiguration, requestAccessToken } from '../utils' export interface OAuthCognitoConfig { /** @@ -42,11 +43,6 @@ export interface OAuthCognitoConfig { * @default process.env.NUXT_OAUTH_COGNITO_REDIRECT_URL or current URL */ redirectURL?: string - /** - * AWS Cognito App Custom Domain – some pool configurations require this - * @default '' - */ - domain?: string } export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }: OAuthConfig) { @@ -59,11 +55,16 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }: O return handleMissingConfiguration(event, 'cognito', ['clientId', 'clientSecret', 'userPoolId', 'region'], onError) } - const urlBase = config?.domain || `${config.userPoolId}.auth.${config.region}.amazoncognito.com` - - const authorizationURL = `https://${urlBase}/oauth2/authorize` - const tokenURL = `https://${urlBase}/oauth2/token` - + const congitoDiscoveryUrl = new URL(`https://cognito-idp.${config.region}.amazonaws.com/${config.userPoolId}/.well-known/openid-configuration`) + const issuer = await discovery(congitoDiscoveryUrl, config.clientId, config.clientSecret) + const { + authorization_endpoint: authorizationURL, + token_endpoint: tokenURL, + userinfo_endpoint: userinfoURL, + // TODO: implement logout + // eslint-disable-next-line @typescript-eslint/no-unused-vars + end_session_endpoint: logoutURL, + } = issuer.serverMetadata() const query = getQuery<{ code?: string }>(event) const redirectURL = config.redirectURL || getOAuthRedirectURL(event) @@ -101,9 +102,8 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }: O const tokenType = tokens.token_type const accessToken = tokens.access_token - // TODO: improve typing - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const user: any = await $fetch(`https://${urlBase}/oauth2/userInfo`, { + // TODO: improve typing of user profile + const user: unknown = await $fetch(userinfoURL as string, { headers: { Authorization: `${tokenType} ${accessToken}`, },