Skip to content

Commit

Permalink
feat: add xsuaa provider
Browse files Browse the repository at this point in the history
* feat: add XSUAA OAuth provider

* feat: integrate XSUAA provider

* docs: add XSUAA provider

* feat: add XSUAA provider to playground

This is not tested

* chore: use $fetch

* [autofix.ci] apply automated fixes

* Update playground/.env.example

---------

Co-authored-by: Jan Fröhlich <[email protected]>
Co-authored-by: Sébastien Chopin <[email protected]>
Co-authored-by: Sébastien Chopin <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
5 people authored Jul 7, 2024
1 parent b452d60 commit 9afb9eb
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Add Authentication to Nuxt applications with secured & sealed cookies sessions.
## Features

- [Hybrid Rendering](#hybrid-rendering) support (SSR / CSR / SWR / Prerendering)
- [15 OAuth Providers](#supported-oauth-providers)
- [15+ OAuth Providers](#supported-oauth-providers)
- [Vue composable](#vue-composable)
- [Server utils](#server-utils)
- [`<AuthState>` component](#authstate-component)
Expand Down Expand Up @@ -165,6 +165,7 @@ It can also be set using environment variables:
- Steam
- Twitch
- X (Twitter)
- XSUAA

You can add your favorite provider by creating a new file in [src/runtime/server/lib/oauth/](./src/runtime/server/lib/oauth/).

Expand Down
4 changes: 4 additions & 0 deletions playground/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ NUXT_OAUTH_STEAM_API_KEY=
# X
NUXT_OAUTH_X_CLIENT_ID=
NUXT_OAUTH_X_CLIENT_SECRET=
# XSUAA
NUXT_OAUTH_XSUAA_CLIENT_ID=
NUXT_OAUTH_XSUAA_CLIENT_SECRET=
NUXT_OAUTH_XSUAA_DOMAIN=
6 changes: 6 additions & 0 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ const providers = computed(() => [
disabled: Boolean(user.value?.x),
icon: 'i-simple-icons-x',
},
{
label: user.value?.xsuaa || 'XSUAA',
to: '/auth/xsuaa',
disabled: Boolean(user.value?.xsuaa),
icon: 'i-simple-icons-sap',
},
].map(p => ({
...p,
prefetch: false,
Expand Down
1 change: 1 addition & 0 deletions playground/auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ declare module '#auth-utils' {
paypal?: string
steam?: string
x?: string
xsuaa?: string
}

interface UserSession {
Expand Down
12 changes: 12 additions & 0 deletions playground/server/routes/auth/xsuaa.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default oauth.xsuaaEventHandler({
async onSuccess(event, { user }) {
await setUserSession(event, {
user: {
xsuaa: user.email,
},
loggedInAt: Date.now(),
})

return sendRedirect(event, '/')
},
})
6 changes: 6 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,11 @@ export default defineNuxtModule<ModuleOptions>({
clientId: '',
clientSecret: '',
})
// XSUAA OAuth
runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
clientId: '',
clientSecret: '',
domain: '',
})
},
})
110 changes: 110 additions & 0 deletions src/runtime/server/lib/oauth/xsuaa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import type { H3Event } from 'h3'
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from 'h3'
import { withQuery, parsePath } from 'ufo'
import { defu } from 'defu'
import { useRuntimeConfig } from '#imports'
import type { OAuthConfig } from '#auth-utils'

export interface OAuthXSUAAConfig {
/**
* XSUAA OAuth Client ID
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_ID
*/
clientId?: string
/**
* XSUAA OAuth Client Secret
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_SECRET
*/
clientSecret?: string
/**
* XSUAA OAuth Issuer
* @default process.env.NUXT_OAUTH_XSUAA_DOMAIN
*/
domain?: string
/**
* XSUAA OAuth Scope
* @default []
* @see https://sap.github.io/cloud-sdk/docs/java/guides/cloud-foundry-xsuaa-service
* @example ['openid']
*/
scope?: string[]
}

export function xsuaaEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthXSUAAConfig>) {
return eventHandler(async (event: H3Event) => {
config = defu(config, useRuntimeConfig(event).oauth?.xsuaa) as OAuthXSUAAConfig
const { code } = getQuery(event)

if (!config.clientId || !config.clientSecret || !config.domain) {
const error = createError({
statusCode: 500,
message: 'Missing NUXT_OAUTH_XSUAA_CLIENT_ID or NUXT_OAUTH_XSUAA_CLIENT_SECRET or NUXT_OAUTH_XSUAA_DOMAIN env variables.',
})
if (!onError) throw error
return onError(event, error)
}
const authorizationURL = `https://${config.domain}/oauth/authorize`
const tokenURL = `https://${config.domain}/oauth/token`

const redirectUrl = getRequestURL(event).href
if (!code) {
config.scope = config.scope || []
// Redirect to XSUAA Oauth page
return sendRedirect(
event,
withQuery(authorizationURL as string, {
response_type: 'code',
client_id: config.clientId,
redirect_uri: redirectUrl,
scope: config.scope.join(' '),
}),
)
}

// TODO: improve typing
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const tokens: any = await $fetch(
tokenURL as string,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: config.clientId,
client_secret: config.clientSecret,
redirect_uri: parsePath(redirectUrl).pathname,
code: `${code}`,
}),
},
).catch((error) => {
return { error }
})
if (tokens.error) {
const error = createError({
statusCode: 401,
message: `XSUAA login failed: ${tokens.error?.data?.error_description || 'Unknown error'}`,
data: tokens,
})
if (!onError) throw error
return onError(event, error)
}

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://${config.domain}/userinfo`, {
headers: {
Authorization: `${tokenType} ${accessToken}`,
},
})

return onSuccess(event, {
tokens,
user,
})
})
}
2 changes: 2 additions & 0 deletions src/runtime/server/utils/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { facebookEventHandler } from '../lib/oauth/facebook'
import { paypalEventHandler } from '../lib/oauth/paypal'
import { steamEventHandler } from '../lib/oauth/steam'
import { xEventHandler } from '../lib/oauth/x'
import { xsuaaEventHandler } from '../lib/oauth/xsuaa'

export const oauth = {
githubEventHandler,
Expand All @@ -30,4 +31,5 @@ export const oauth = {
paypalEventHandler,
steamEventHandler,
xEventHandler,
xsuaaEventHandler,
}

0 comments on commit 9afb9eb

Please sign in to comment.