mirror of
https://github.com/wg-easy/wg-easy.git
synced 2026-07-09 09:24:18 +00:00
support generic oidc
This commit is contained in:
parent
d20b93156b
commit
c4524fbd44
8 changed files with 116 additions and 21 deletions
|
|
@ -8,10 +8,11 @@ title: External Authentication
|
|||
|
||||
To enable OAuth set the env var `OAUTH_PROVIDERS` to any of the following providers:
|
||||
|
||||
| Provider | Value |
|
||||
| ----------------- | -------- |
|
||||
| [Google](#google) | `google` |
|
||||
| [GitHub](#github) | `github` |
|
||||
| Provider | Value |
|
||||
| ----------------------------- | -------- |
|
||||
| [Google](#google) | `google` |
|
||||
| [GitHub](#github) | `github` |
|
||||
| [Generic OIDC](#generic-oidc) | `oidc` |
|
||||
|
||||
You can enable multiple providers by separating them with a comma:
|
||||
|
||||
|
|
@ -41,9 +42,52 @@ e.g. `google,github`
|
|||
| `OAUTH_GITHUB_CLIENT_ID` | ✔️ | `xxx` | GitHub Client ID |
|
||||
| `OAUTH_GITHUB_CLIENT_SECRET` | ✔️ | `xxx` | GitHub Client Secret |
|
||||
|
||||
<!-- TODO Github Setup -->
|
||||
|
||||
### Generic OIDC
|
||||
|
||||
TODO
|
||||
This supports generic OIDC providers like Authelia, Authentik, etc.
|
||||
|
||||
The provider needs to support:
|
||||
|
||||
- PKCE
|
||||
- default scopes: `openid email profile`
|
||||
- Valid HTTPS
|
||||
- Client Secret Authentication `client_secret_post`
|
||||
|
||||
| Env | Required | Default | Example | Description |
|
||||
| -------------------------- | -------- | ------- | -------------------------- | ------------------ |
|
||||
| `OAUTH_OIDC_SERVER` | ✔️ | - | `https://auth.example.com` | OIDC Server |
|
||||
| `OAUTH_OIDC_CLIENT_ID` | ✔️ | - | `xxx` | OIDC Client ID |
|
||||
| `OAUTH_OIDC_CLIENT_SECRET` | ✔️ | - | `xxx` | OIDC Client Secret |
|
||||
| `OAUTH_OIDC_NAME` | ✖️ | OIDC | `Authelia` | Provider Name |
|
||||
|
||||
#### Authelia Setup
|
||||
|
||||
Generate Client ID and Secret:
|
||||
|
||||
```shell
|
||||
# Client ID
|
||||
docker run --rm authelia/authelia:latest authelia crypto rand --length 72 --charset rfc3986
|
||||
# Client Secret
|
||||
docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
|
||||
```
|
||||
|
||||
```yaml
|
||||
- client_id: '...'
|
||||
client_name: wg-easy
|
||||
client_secret: '$pbkdf2-...'
|
||||
redirect_uris:
|
||||
- https://<your-domain>/api/auth/oidc/callback
|
||||
scopes:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
authorization_policy: one_factor
|
||||
pre_configured_consent_duration: 1 week
|
||||
require_pkce: true
|
||||
token_endpoint_auth_method: client_secret_post
|
||||
```
|
||||
|
||||
### Generic OAuth
|
||||
|
||||
|
|
|
|||
|
|
@ -15,29 +15,53 @@
|
|||
<div v-if="authMethods" class="flex flex-col gap-5">
|
||||
<!-- Google OAuth Button -->
|
||||
<a
|
||||
v-if="authMethods.providers?.google"
|
||||
v-if="authMethods.providers?.google?.enabled"
|
||||
href="/api/auth/google"
|
||||
class="flex cursor-pointer items-center justify-center gap-2 rounded border border-gray-300 bg-white py-2 text-sm text-gray-700 shadow-sm transition hover:bg-gray-50 dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<IconsBrandsGoogle class="h-4 w-4" />
|
||||
<span>{{ $t('login.signInWith', ['Google']) }}</span>
|
||||
<span>
|
||||
{{
|
||||
$t('login.signInWith', [
|
||||
authMethods.providers.google.friendlyName,
|
||||
])
|
||||
}}
|
||||
</span>
|
||||
</a>
|
||||
<!-- GitHub OAuth Button -->
|
||||
<a
|
||||
v-if="authMethods.providers?.github"
|
||||
v-if="authMethods.providers?.github?.enabled"
|
||||
href="/api/auth/github"
|
||||
class="flex cursor-pointer items-center justify-center gap-2 rounded border border-gray-300 bg-white py-2 text-sm text-gray-700 shadow-sm transition hover:bg-gray-50 dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<IconsBrandsGitHub class="h-4 w-4" />
|
||||
<span>{{ $t('login.signInWith', ['GitHub']) }}</span>
|
||||
<span>
|
||||
{{
|
||||
$t('login.signInWith', [
|
||||
authMethods.providers.github.friendlyName,
|
||||
])
|
||||
}}
|
||||
</span>
|
||||
</a>
|
||||
<!-- OIDC OAuth Button -->
|
||||
<a
|
||||
v-if="authMethods.providers?.oidc?.enabled"
|
||||
href="/api/auth/oidc"
|
||||
class="flex cursor-pointer items-center justify-center rounded border border-gray-300 bg-white py-2 text-sm text-gray-700 shadow-sm transition hover:bg-gray-50 dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<span>
|
||||
{{
|
||||
$t('login.signInWith', [authMethods.providers.oidc.friendlyName])
|
||||
}}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Divider -->
|
||||
<div v-if="authMethods.oauthEnabled" class="flex items-center gap-2">
|
||||
<div class="h-px flex-1 bg-gray-300 dark:bg-neutral-600"></div>
|
||||
<span class="text-xs text-gray-500 dark:text-neutral-400">{{
|
||||
$t('login.or')
|
||||
}}</span>
|
||||
<span class="text-xs text-gray-500 dark:text-neutral-400">
|
||||
{{ $t('login.or') }}
|
||||
</span>
|
||||
<div class="h-px flex-1 bg-gray-300 dark:bg-neutral-600"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default defineEventHandler(async (event) => {
|
|||
expectedNonce:
|
||||
providerConfig.isOIDC === false ? undefined : session.data.oauth_nonce,
|
||||
expectedState: session.data.oauth_state,
|
||||
idTokenExpected: providerConfig.isOIDC,
|
||||
idTokenExpected: providerConfig.isOIDC ?? true,
|
||||
});
|
||||
|
||||
type SubjectType = string | undefined | typeof client.skipSubjectCheck;
|
||||
|
|
@ -45,8 +45,6 @@ export default defineEventHandler(async (event) => {
|
|||
userInfo = await client.fetchUserInfo(config, tokens.access_token, subject);
|
||||
}
|
||||
|
||||
console.log(userInfo);
|
||||
|
||||
if (!userInfo.sub) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@ export default defineEventHandler(() => {
|
|||
return {
|
||||
providers: WG_ENV.OAUTH_PROVIDERS?.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr] = true;
|
||||
acc[curr] = {
|
||||
enabled: true,
|
||||
friendlyName: OAUTH_PROVIDERS[curr].friendlyName,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as Record<OAUTH_PROVIDER, boolean>
|
||||
{} as Partial<
|
||||
Record<OAUTH_PROVIDER, { enabled: true; friendlyName: string }>
|
||||
>
|
||||
),
|
||||
oauthEnabled:
|
||||
WG_ENV.OAUTH_PROVIDERS !== undefined && WG_ENV.OAUTH_PROVIDERS.length > 0,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ CREATE TABLE `__new_users_table` (
|
|||
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_users_table`("id", "username", "password", "email", "name", "role", "totp_key", "totp_verified", "enabled", "oauth_provider", "oauth_id", "created_at", "updated_at") SELECT "id", "username", "password", "email", "name", "role", "totp_key", "totp_verified", "enabled", "oauth_provider", "oauth_id", "created_at", "updated_at" FROM `users_table`;--> statement-breakpoint
|
||||
INSERT INTO `__new_users_table`("id", "username", "password", "email", "name", "role", "totp_key", "totp_verified", "enabled", "created_at", "updated_at") SELECT "id", "username", "password", "email", "name", "role", "totp_key", "totp_verified", "enabled", "created_at", "updated_at" FROM `users_table`;--> statement-breakpoint
|
||||
DROP TABLE `users_table`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_users_table` RENAME TO `users_table`;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "840b00a9-8e9d-4bc9-a0fa-6185ebb01a46",
|
||||
"id": "8cfd2b7c-5483-4b98-b95e-f9c741f32821",
|
||||
"prevId": "0f072f91-cd10-4702-ae7b-245255d69d1e",
|
||||
"tables": {
|
||||
"clients_table": {
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@
|
|||
{
|
||||
"idx": 5,
|
||||
"version": "6",
|
||||
"when": 1779862471761,
|
||||
"tag": "0005_supreme_groot",
|
||||
"when": 1779865249257,
|
||||
"tag": "0005_lean_the_executioner",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { H3Event } from 'h3';
|
|||
import { discovery } from 'openid-client';
|
||||
|
||||
type OAuthConfig = {
|
||||
friendlyName: string;
|
||||
server: string;
|
||||
scope: string;
|
||||
clientId: string | undefined;
|
||||
|
|
@ -12,6 +13,7 @@ type OAuthConfig = {
|
|||
};
|
||||
|
||||
const GoogleConfig: OAuthConfig = {
|
||||
friendlyName: 'Google',
|
||||
server: 'https://accounts.google.com',
|
||||
scope: 'openid email profile',
|
||||
clientId: process.env.OAUTH_GOOGLE_CLIENT_ID,
|
||||
|
|
@ -22,6 +24,7 @@ const GoogleConfig: OAuthConfig = {
|
|||
},
|
||||
};
|
||||
const GithubConfig: OAuthConfig = {
|
||||
friendlyName: 'GitHub',
|
||||
server: 'https://github.com/login/oauth',
|
||||
scope: 'read:user user:email',
|
||||
clientId: process.env.OAUTH_GITHUB_CLIENT_ID,
|
||||
|
|
@ -33,10 +36,19 @@ const GithubConfig: OAuthConfig = {
|
|||
isOIDC: false,
|
||||
userInfoFlow: 'github',
|
||||
};
|
||||
const OidcConfig: OAuthConfig = {
|
||||
friendlyName: process.env.OAUTH_OIDC_NAME ?? 'OIDC',
|
||||
server: process.env.OAUTH_OIDC_SERVER ?? '',
|
||||
scope: 'openid email profile',
|
||||
clientId: process.env.OAUTH_OIDC_CLIENT_ID,
|
||||
clientSecret: process.env.OAUTH_OIDC_CLIENT_SECRET,
|
||||
params: {},
|
||||
};
|
||||
|
||||
export const OAUTH_PROVIDERS = {
|
||||
google: GoogleConfig,
|
||||
github: GithubConfig,
|
||||
oidc: OidcConfig,
|
||||
};
|
||||
|
||||
export type OAUTH_PROVIDER = keyof typeof OAUTH_PROVIDERS;
|
||||
|
|
@ -62,6 +74,11 @@ export function isConfiguredOauthProvider(
|
|||
return true;
|
||||
}
|
||||
|
||||
function isEnabledProvider(provider: OAUTH_PROVIDER) {
|
||||
return WG_ENV.OAUTH_PROVIDERS?.includes(provider);
|
||||
}
|
||||
|
||||
// TODO: simplify logic between WG_ENV.OAUTH_PROVIDERS and buildOauthConfig
|
||||
export async function buildOauthConfig(event: H3Event) {
|
||||
const provider = getRouterParam(event, 'provider');
|
||||
if (!provider || !isValidOauthProvider(provider)) {
|
||||
|
|
@ -71,6 +88,13 @@ export async function buildOauthConfig(event: H3Event) {
|
|||
});
|
||||
}
|
||||
|
||||
if (!isEnabledProvider(provider)) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Disabled provider',
|
||||
});
|
||||
}
|
||||
|
||||
const oauthProvider = OAUTH_PROVIDERS[provider];
|
||||
|
||||
if (!isConfiguredOauthProvider(oauthProvider)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue