Skip to content

Commit 9afb9eb

Browse files
janfrlatinuxautofix-ci[bot]
authored
feat: add xsuaa provider
* 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>
1 parent b452d60 commit 9afb9eb

File tree

8 files changed

+143
-1
lines changed

8 files changed

+143
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Add Authentication to Nuxt applications with secured & sealed cookies sessions.
1515
## Features
1616

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

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

playground/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ NUXT_OAUTH_STEAM_API_KEY=
4949
# X
5050
NUXT_OAUTH_X_CLIENT_ID=
5151
NUXT_OAUTH_X_CLIENT_SECRET=
52+
# XSUAA
53+
NUXT_OAUTH_XSUAA_CLIENT_ID=
54+
NUXT_OAUTH_XSUAA_CLIENT_SECRET=
55+
NUXT_OAUTH_XSUAA_DOMAIN=

playground/app.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ const providers = computed(() => [
119119
disabled: Boolean(user.value?.x),
120120
icon: 'i-simple-icons-x',
121121
},
122+
{
123+
label: user.value?.xsuaa || 'XSUAA',
124+
to: '/auth/xsuaa',
125+
disabled: Boolean(user.value?.xsuaa),
126+
icon: 'i-simple-icons-sap',
127+
},
122128
].map(p => ({
123129
...p,
124130
prefetch: false,

playground/auth.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ declare module '#auth-utils' {
1616
paypal?: string
1717
steam?: string
1818
x?: string
19+
xsuaa?: string
1920
}
2021

2122
interface UserSession {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default oauth.xsuaaEventHandler({
2+
async onSuccess(event, { user }) {
3+
await setUserSession(event, {
4+
user: {
5+
xsuaa: user.email,
6+
},
7+
loggedInAt: Date.now(),
8+
})
9+
10+
return sendRedirect(event, '/')
11+
},
12+
})

src/module.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,11 @@ export default defineNuxtModule<ModuleOptions>({
146146
clientId: '',
147147
clientSecret: '',
148148
})
149+
// XSUAA OAuth
150+
runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
151+
clientId: '',
152+
clientSecret: '',
153+
domain: '',
154+
})
149155
},
150156
})

src/runtime/server/lib/oauth/xsuaa.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import type { H3Event } from 'h3'
2+
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from 'h3'
3+
import { withQuery, parsePath } from 'ufo'
4+
import { defu } from 'defu'
5+
import { useRuntimeConfig } from '#imports'
6+
import type { OAuthConfig } from '#auth-utils'
7+
8+
export interface OAuthXSUAAConfig {
9+
/**
10+
* XSUAA OAuth Client ID
11+
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_ID
12+
*/
13+
clientId?: string
14+
/**
15+
* XSUAA OAuth Client Secret
16+
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_SECRET
17+
*/
18+
clientSecret?: string
19+
/**
20+
* XSUAA OAuth Issuer
21+
* @default process.env.NUXT_OAUTH_XSUAA_DOMAIN
22+
*/
23+
domain?: string
24+
/**
25+
* XSUAA OAuth Scope
26+
* @default []
27+
* @see https://sap.github.io/cloud-sdk/docs/java/guides/cloud-foundry-xsuaa-service
28+
* @example ['openid']
29+
*/
30+
scope?: string[]
31+
}
32+
33+
export function xsuaaEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthXSUAAConfig>) {
34+
return eventHandler(async (event: H3Event) => {
35+
config = defu(config, useRuntimeConfig(event).oauth?.xsuaa) as OAuthXSUAAConfig
36+
const { code } = getQuery(event)
37+
38+
if (!config.clientId || !config.clientSecret || !config.domain) {
39+
const error = createError({
40+
statusCode: 500,
41+
message: 'Missing NUXT_OAUTH_XSUAA_CLIENT_ID or NUXT_OAUTH_XSUAA_CLIENT_SECRET or NUXT_OAUTH_XSUAA_DOMAIN env variables.',
42+
})
43+
if (!onError) throw error
44+
return onError(event, error)
45+
}
46+
const authorizationURL = `https://${config.domain}/oauth/authorize`
47+
const tokenURL = `https://${config.domain}/oauth/token`
48+
49+
const redirectUrl = getRequestURL(event).href
50+
if (!code) {
51+
config.scope = config.scope || []
52+
// Redirect to XSUAA Oauth page
53+
return sendRedirect(
54+
event,
55+
withQuery(authorizationURL as string, {
56+
response_type: 'code',
57+
client_id: config.clientId,
58+
redirect_uri: redirectUrl,
59+
scope: config.scope.join(' '),
60+
}),
61+
)
62+
}
63+
64+
// TODO: improve typing
65+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
66+
const tokens: any = await $fetch(
67+
tokenURL as string,
68+
{
69+
method: 'POST',
70+
headers: {
71+
'Content-Type': 'application/x-www-form-urlencoded',
72+
},
73+
body: new URLSearchParams({
74+
grant_type: 'authorization_code',
75+
client_id: config.clientId,
76+
client_secret: config.clientSecret,
77+
redirect_uri: parsePath(redirectUrl).pathname,
78+
code: `${code}`,
79+
}),
80+
},
81+
).catch((error) => {
82+
return { error }
83+
})
84+
if (tokens.error) {
85+
const error = createError({
86+
statusCode: 401,
87+
message: `XSUAA login failed: ${tokens.error?.data?.error_description || 'Unknown error'}`,
88+
data: tokens,
89+
})
90+
if (!onError) throw error
91+
return onError(event, error)
92+
}
93+
94+
const tokenType = tokens.token_type
95+
const accessToken = tokens.access_token
96+
97+
// TODO: improve typing
98+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
99+
const user: any = await $fetch(`https://${config.domain}/userinfo`, {
100+
headers: {
101+
Authorization: `${tokenType} ${accessToken}`,
102+
},
103+
})
104+
105+
return onSuccess(event, {
106+
tokens,
107+
user,
108+
})
109+
})
110+
}

src/runtime/server/utils/oauth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { facebookEventHandler } from '../lib/oauth/facebook'
1313
import { paypalEventHandler } from '../lib/oauth/paypal'
1414
import { steamEventHandler } from '../lib/oauth/steam'
1515
import { xEventHandler } from '../lib/oauth/x'
16+
import { xsuaaEventHandler } from '../lib/oauth/xsuaa'
1617

1718
export const oauth = {
1819
githubEventHandler,
@@ -30,4 +31,5 @@ export const oauth = {
3031
paypalEventHandler,
3132
steamEventHandler,
3233
xEventHandler,
34+
xsuaaEventHandler,
3335
}

0 commit comments

Comments
 (0)