From e4286d1ccada297a1b9e861159bcc80b6012c472 Mon Sep 17 00:00:00 2001 From: Bernd Storath Date: Fri, 29 May 2026 14:52:41 +0200 Subject: [PATCH] support allowed domains --- .../config/external-authentication.md | 2 - .../api/auth/[provider]/callback.get.ts | 21 -------- src/server/api/auth/[provider]/link.get.ts | 7 --- src/server/plugins/manager.ts | 18 ++++--- src/server/utils/config.ts | 9 ++++ src/server/utils/oauth.ts | 54 +++++++++++++++++++ 6 files changed, 73 insertions(+), 38 deletions(-) diff --git a/docs/content/advanced/config/external-authentication.md b/docs/content/advanced/config/external-authentication.md index ae2d5839..1243f58c 100644 --- a/docs/content/advanced/config/external-authentication.md +++ b/docs/content/advanced/config/external-authentication.md @@ -45,8 +45,6 @@ Use [Allowed Domains](#allowed-domains) to restrict which users can log in. /// - - ### Allowed Domains To only allow users with an email address from a specific domain to log in, set the env var `OAUTH_ALLOWED_DOMAINS` to the allowed domain. diff --git a/src/server/api/auth/[provider]/callback.get.ts b/src/server/api/auth/[provider]/callback.get.ts index 225a7b96..3f8b5252 100644 --- a/src/server/api/auth/[provider]/callback.get.ts +++ b/src/server/api/auth/[provider]/callback.get.ts @@ -24,27 +24,6 @@ export default defineEventHandler(async (event) => { providerConfig ); - if (!userInfo.sub) { - throw createError({ - statusCode: 400, - statusMessage: 'No sub set', - }); - } - - if (!userInfo.email) { - throw createError({ - statusCode: 400, - statusMessage: 'No email set', - }); - } - - if (!userInfo.email_verified) { - throw createError({ - statusCode: 401, - statusMessage: 'Email is not verified', - }); - } - const result = await Database.users.findOrCreateByProvider( provider, userInfo.sub, diff --git a/src/server/api/auth/[provider]/link.get.ts b/src/server/api/auth/[provider]/link.get.ts index 3c3c7d66..51b6191d 100644 --- a/src/server/api/auth/[provider]/link.get.ts +++ b/src/server/api/auth/[provider]/link.get.ts @@ -29,13 +29,6 @@ export default definePermissionEventHandler( providerConfig ); - if (!userInfo.sub) { - throw createError({ - statusCode: 400, - statusMessage: 'No sub set', - }); - } - await Database.users.linkOauth(user.id, provider, userInfo.sub); return sendRedirect(event, '/me'); diff --git a/src/server/plugins/manager.ts b/src/server/plugins/manager.ts index 701e052e..f12811b5 100644 --- a/src/server/plugins/manager.ts +++ b/src/server/plugins/manager.ts @@ -1,12 +1,14 @@ export default defineNitroPlugin((nitroApp) => { - console.log(`====================================================`); - console.log(` wg-easy - https://github.com/wg-easy/wg-easy `); - console.log(`====================================================`); - console.log(`| wg-easy: ${RELEASE.padEnd(38)} |`); - console.log(`| Node: ${process.version.padEnd(38)} |`); - console.log(`| Platform: ${process.platform.padEnd(38)} |`); - console.log(`| Arch: ${process.arch.padEnd(38)} |`); - console.log(`====================================================`); + console.log(` +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ wg-easy - https://github.com/wg-easy/wg-easy ┃ +┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ +┃ wg-easy: ${RELEASE.padEnd(38)} ┃ +┃ Node: ${process.version.padEnd(38)} ┃ +┃ Platform: ${process.platform.padEnd(38)} ┃ +┃ Arch: ${process.arch.padEnd(38)} ┃ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +`); nitroApp.hooks.hook('close', async () => { console.log('Shutting down'); await WireGuard.Shutdown(); diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index b085c3a6..ad6e80dd 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -42,8 +42,17 @@ export const WG_ENV = { .map((v) => v.trim()) .filter((v) => isValidOauthProvider(v)) .filter((v) => isConfiguredOauthProvider(OAUTH_PROVIDERS[v])), + OAUTH_ALLOWED_DOMAINS: process.env.OAUTH_ALLOWED_DOMAINS?.split(',').map( + (v) => v.trim() + ), }; +if (WG_ENV.OAUTH_PROVIDERS && WG_ENV.OAUTH_PROVIDERS.length > 1) { + SERVER_DEBUG(` +Enabled OAuth providers: ${WG_ENV.OAUTH_PROVIDERS.join(', ')} +Allowed OAuth domains: ${WG_ENV.OAUTH_ALLOWED_DOMAINS?.join(', ') ?? 'All'}`); +} + export const WG_INITIAL_ENV = { ENABLED: process.env.INIT_ENABLED === 'true', USERNAME: process.env.INIT_USERNAME, diff --git a/src/server/utils/oauth.ts b/src/server/utils/oauth.ts index f6b17da7..14036e19 100644 --- a/src/server/utils/oauth.ts +++ b/src/server/utils/oauth.ts @@ -208,5 +208,59 @@ export async function getUserInfo( userInfo = await client.fetchUserInfo(config, tokens.access_token, subject); } + if (!hasOauthProps(userInfo)) { + throw createError({ + statusCode: 400, + statusMessage: 'Invalid user info', + }); + } + + if (!isAllowedDomain(userInfo.email)) { + throw createError({ + statusCode: 401, + statusMessage: 'Email domain not allowed', + }); + } + return userInfo; } + +function hasOauthProps< + T extends { sub?: string; email?: string; email_verified?: boolean }, +>( + userInfo: T +): userInfo is T & { sub: string; email: string; email_verified: boolean } { + if (!userInfo.sub) { + throw createError({ + statusCode: 400, + statusMessage: 'No sub set', + }); + } + + if (!userInfo.email) { + throw createError({ + statusCode: 400, + statusMessage: 'No email set', + }); + } + + if (!userInfo.email_verified) { + throw createError({ + statusCode: 401, + statusMessage: 'Email is not verified', + }); + } + + return true; +} + +function isAllowedDomain(email: string) { + const emailDomain = email.slice(email.lastIndexOf('@') + 1); + if ( + WG_ENV.OAUTH_ALLOWED_DOMAINS && + !WG_ENV.OAUTH_ALLOWED_DOMAINS.includes(emailDomain) + ) { + return false; + } + return true; +}