Skip to content

Commit

Permalink
feat(provider): ✨ Added AWS Cognito provider
Browse files Browse the repository at this point in the history
  • Loading branch information
itpropro committed Sep 23, 2024
1 parent 8605ba4 commit 3da9e5d
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 14 deletions.
5 changes: 5 additions & 0 deletions playground/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ NUXT_OIDC_PROVIDERS_GITHUB_CLIENT_ID=
NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET=
NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID=
NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL=
# COGNITO PROVIDER CONFIG
NUXT_OIDC_PROVIDERS_COGNITO_CLIENT_ID=
NUXT_OIDC_PROVIDERS_COGNITO_CLIENT_SECRET=
NUXT_OIDC_PROVIDERS_COGNITO_BASE_URL=
NUXT_OIDC_PROVIDERS_COGNITO_OPEN_ID_CONFIGURATION=
6 changes: 6 additions & 0 deletions playground/composables/useProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export function useProviders(currentProvider: string) {
disabled: Boolean(currentProvider === 'keycloak'),
icon: 'i-simple-icons-cncf',
},
{
label: 'AWS Cognito',
name: 'cognito',
disabled: Boolean(currentProvider === 'cognito'),
icon: 'i-simple-icons-amazoncognito',
},
{
label: 'Generic OIDC',
name: 'oidc',
Expand Down
9 changes: 9 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ export default defineNuxtConfig({
exposeAccessToken: true,
userNameClaim: 'preferred_username',
},
cognito: {
clientId: '',
redirectUri: 'http://localhost:3000/auth/cognito/callback',
clientSecret: '',
scope: ['openid', 'email', 'profile'],
logoutRedirectUri: 'https://google.com',
baseUrl: '',
openIdConfiguration: '',
}
},
session: {
expirationCheck: true,
Expand Down
37 changes: 37 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,46 @@ export default defineNuxtModule<ModuleOptions>({
if (baseUrl) {
(options.providers[provider] as OidcProviderConfig).authorizationUrl = generateProviderUrl(baseUrl as string, providerPresets[provider].authorizationUrl);
(options.providers[provider] as OidcProviderConfig).tokenUrl = generateProviderUrl(baseUrl as string, providerPresets[provider].tokenUrl);
(options.providers[provider] as OidcProviderConfig).logoutUrl = generateProviderUrl(baseUrl as string, providerPresets[provider].logoutUrl);
(options.providers[provider] as OidcProviderConfig).userInfoUrl = generateProviderUrl(baseUrl as string, providerPresets[provider].userInfoUrl)
}

// Replace clientId in additionalAuthParameters
if (providerPresets[provider].additionalAuthParameters) {
const entry = providerPresets[provider].additionalAuthParameters as Record<string, string>
Object.keys(entry).forEach((param) => {
if ((entry[param] as string).includes('{clientId}')) {
if (!(options.providers[provider] as OidcProviderConfig).additionalAuthParameters)
(options.providers[provider] as OidcProviderConfig).additionalAuthParameters = {};
(options.providers[provider] as OidcProviderConfig).additionalAuthParameters![param] = entry[param].replace('{clientId}', (options.providers[provider] as OidcProviderConfig).clientId || process.env[`NUXT_OIDC_PROVIDERS_${provider.toUpperCase()}_CLIENT_ID`] || '')
}
})
}

// Replace clientId in additionalTokenParameters
if (providerPresets[provider].additionalTokenParameters) {
const entry = providerPresets[provider].additionalTokenParameters as Record<string, string>
Object.keys(entry).forEach((param) => {
if ((entry[param] as string).includes('{clientId}')) {
if (!(options.providers[provider] as OidcProviderConfig).additionalTokenParameters)
(options.providers[provider] as OidcProviderConfig).additionalTokenParameters = {};
(options.providers[provider] as OidcProviderConfig).additionalTokenParameters![param] = entry[param].replace('{clientId}', (options.providers[provider] as OidcProviderConfig).clientId || process.env[`NUXT_OIDC_PROVIDERS_${provider.toUpperCase()}_CLIENT_ID`] || '')
}
})
}

// Replace clientId in additionalLogoutParameters
if (providerPresets[provider].additionalLogoutParameters) {
const entry = providerPresets[provider].additionalLogoutParameters as Record<string, string>
Object.keys(entry).forEach((param) => {
if ((entry[param] as string).includes('{clientId}')) {
if (!(options.providers[provider] as OidcProviderConfig).additionalLogoutParameters)
(options.providers[provider] as OidcProviderConfig).additionalLogoutParameters = {};
(options.providers[provider] as OidcProviderConfig).additionalLogoutParameters![param] = entry[param].replace('{clientId}', (options.providers[provider] as OidcProviderConfig).clientId || process.env[`NUXT_OIDC_PROVIDERS_${provider.toUpperCase()}_CLIENT_ID`] || '')
}
})
}

// Add login handler
addServerHandler({
handler: resolve('./runtime/server/handler/login.get'),
Expand Down
50 changes: 50 additions & 0 deletions src/runtime/providers/cognito.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ofetch } from 'ofetch'
import { normalizeURL, withHttps, withoutTrailingSlash } from 'ufo'
import { defineOidcProvider } from '../server/utils/provider'

interface CognitoProviderConfig {
connection?: string
organization?: string
invitation?: string
loginHint?: string
}

type CognitoRequiredFields = 'baseUrl' | 'clientId' | 'clientSecret' | 'logoutRedirectUri' | 'openIdConfiguration'

export const cognito = defineOidcProvider<CognitoProviderConfig, CognitoRequiredFields>({
userNameClaim: 'username',
responseType: 'code',
tokenRequestType: 'form-urlencoded',
authenticationScheme: 'header',
userInfoUrl: 'oauth2/userInfo',
grantType: 'authorization_code',
scope: ['openid'],
pkce: true,
state: true,
nonce: true,
scopeInTokenRequest: false,
callbackRedirectUrl: '/',
authorizationUrl: 'oauth2/authorize',
tokenUrl: 'oauth2/token',
logoutUrl: 'logout',
requiredProperties: [
'baseUrl',
'clientId',
'clientSecret',
'authorizationUrl',
'tokenUrl',
'logoutRedirectUri',
'openIdConfiguration',
],
validateAccessToken: true,
validateIdToken: true,
additionalLogoutParameters: {
clientId: '{clientId}',
},
sessionConfiguration: {
expirationCheck: true,
automaticRefresh: true,
expirationThreshold: 240,
},
logoutRedirectParameterName: 'logout_uri',
})
1 change: 1 addition & 0 deletions src/runtime/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { apple } from './apple.js'
export { auth0 } from './auth0.js'
export { cognito } from './cognito.js'
export { entra } from './entra.js'
export { github } from './github.js'
export { keycloak } from './keycloak.js'
Expand Down
22 changes: 8 additions & 14 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,17 @@ import type * as _PROVIDERS from './providers'

import type { EncryptedToken, JwtPayload } from './server/utils/security'

export type ProviderKeys = 'apple' | 'auth0' | 'entra' | 'github' | 'keycloak' | 'oidc'
export type ProviderKeys = 'apple' | 'auth0' | 'entra' | 'github' | 'keycloak' | 'oidc' | 'cognito'
export type ProviderKeysWithDev = ProviderKeys | 'dev'

type Auth0Provider = typeof _PROVIDERS.auth0
type AppleProvider = typeof _PROVIDERS.apple
type EntraProvider = typeof _PROVIDERS.entra
type GithubProvider = typeof _PROVIDERS.github
type KeycloakProvider = typeof _PROVIDERS.keycloak
type OidcProvider = typeof _PROVIDERS.oidc

export interface ProviderConfigs {
auth0: Auth0Provider
apple: AppleProvider
entra: EntraProvider
github: GithubProvider
keycloak: KeycloakProvider
oidc: OidcProvider
auth0: typeof _PROVIDERS.auth0
apple: typeof _PROVIDERS.apple
entra: typeof _PROVIDERS.entra
github: typeof _PROVIDERS.github
keycloak: typeof _PROVIDERS.keycloak
oidc: typeof _PROVIDERS.oidc
cognito: typeof _PROVIDERS.cognito
}

export interface OAuthConfig<UserSession> {
Expand Down

0 comments on commit 3da9e5d

Please sign in to comment.