From cc358702b8044faaa264db1550d41b3b844d5e24 Mon Sep 17 00:00:00 2001 From: DrummyFloyd Date: Wed, 15 Jan 2025 15:40:23 +0100 Subject: [PATCH 1/2] feat(oidc): add authentik as oidc chore(env): missing var in .env.example chore: typo chore: add reviewed stuff fix: rebased stuff fix(authentik-oidc): redirect corect oidc provider --- .env.example | 5 ++ .../auth/providers/authentik.provider.ts | 88 +++++++++++++++++++ .../auth/providers/providers.factory.ts | 3 + apps/frontend/public/icons/authentik.svg | 1 + apps/frontend/src/app/layout.tsx | 1 + apps/frontend/src/components/auth/login.tsx | 8 +- .../auth/providers/authentik.provider.tsx | 40 +++++++++ apps/frontend/src/middleware.ts | 4 +- .../src/database/prisma/schema.prisma | 3 +- .../src/helpers/variable.context.tsx | 8 +- 10 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 apps/backend/src/services/auth/providers/authentik.provider.ts create mode 100644 apps/frontend/public/icons/authentik.svg create mode 100644 apps/frontend/src/components/auth/providers/authentik.provider.tsx diff --git a/.env.example b/.env.example index d1144f664..fd7dd7433 100644 --- a/.env.example +++ b/.env.example @@ -91,3 +91,8 @@ STRIPE_SIGNING_KEY_CONNECT="" # Developer Settings NX_ADD_PLUGINS=false IS_GENERAL="true" # required for now +AUTHENTIK_OIDC="false" +AUTHENTIK_URL="" +AUTHENTIK_CLIENT_ID="" +AUTHENTIK_CLIENT_SECRET="" +AUTHENTIK_SCOPE="" diff --git a/apps/backend/src/services/auth/providers/authentik.provider.ts b/apps/backend/src/services/auth/providers/authentik.provider.ts new file mode 100644 index 000000000..88f1c1d1b --- /dev/null +++ b/apps/backend/src/services/auth/providers/authentik.provider.ts @@ -0,0 +1,88 @@ +import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface'; + +export class AuthentikProvider implements ProvidersInterface { + private readonly baseUrl: string; + private readonly clientId: string; + private readonly clientSecret: string; + private readonly frontendUrl: string; + + constructor() { + const { + AUTHENTIK_URL, + AUTHENTIK_CLIENT_ID, + AUTHENTIK_CLIENT_SECRET, + FRONTEND_URL, + } = process.env; + + if (!AUTHENTIK_URL) + throw new Error('AUTHENTIK_URL environment variable is not set'); + if (!AUTHENTIK_CLIENT_ID) + throw new Error('AUTHENTIK_CLIENT_ID environment variable is not set'); + if (!AUTHENTIK_CLIENT_SECRET) + throw new Error( + 'AUTHENTIK_CLIENT_SECRET environment variable is not set' + ); + if (!FRONTEND_URL) + throw new Error('FRONTEND_URL environment variable is not set'); + + this.baseUrl = AUTHENTIK_URL.endsWith('/') + ? AUTHENTIK_URL.slice(0, -1) + : AUTHENTIK_URL; + this.clientId = AUTHENTIK_CLIENT_ID; + this.clientSecret = AUTHENTIK_CLIENT_SECRET; + this.frontendUrl = FRONTEND_URL; + } + + generateLink(): string { + const params = new URLSearchParams({ + client_id: this.clientId, + scope: 'openid profile email', + response_type: 'code', + redirect_uri: `${this.frontendUrl}/settings`, + }); + + return `${this.baseUrl}/application/o/authorize/?${params.toString()}`; + } + + async getToken(code: string): Promise { + const response = await fetch(`${this.baseUrl}/application/o/token/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/json', + }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + client_id: this.clientId, + client_secret: this.clientSecret, + code, + redirect_uri: `${this.frontendUrl}/settings`, + }), + }); + + if (!response.ok) { + const error = await response.text(); + throw new Error(`Token request failed: ${error}`); + } + + const { access_token } = await response.json(); + return access_token; + } + + async getUser(access_token: string): Promise<{ email: string; id: string }> { + const response = await fetch(`${this.baseUrl}/application/o/userinfo/`, { + headers: { + Authorization: `Bearer ${access_token}`, + Accept: 'application/json', + }, + }); + + if (!response.ok) { + const error = await response.text(); + throw new Error(`User info request failed: ${error}`); + } + + const { email, sub: id } = await response.json(); + return { email, id }; + } +} diff --git a/apps/backend/src/services/auth/providers/providers.factory.ts b/apps/backend/src/services/auth/providers/providers.factory.ts index 0864e722e..c2d628642 100644 --- a/apps/backend/src/services/auth/providers/providers.factory.ts +++ b/apps/backend/src/services/auth/providers/providers.factory.ts @@ -3,6 +3,7 @@ import { GithubProvider } from '@gitroom/backend/services/auth/providers/github. import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface'; import { GoogleProvider } from '@gitroom/backend/services/auth/providers/google.provider'; import { FarcasterProvider } from '@gitroom/backend/services/auth/providers/farcaster.provider'; +import { AuthentikProvider } from '@gitroom/backend/services/auth/providers/authentik.provider'; export class ProvidersFactory { static loadProvider(provider: Provider): ProvidersInterface { @@ -13,6 +14,8 @@ export class ProvidersFactory { return new GoogleProvider(); case Provider.FARCASTER: return new FarcasterProvider(); + case Provider.AUTHENTIK: + return new AuthentikProvider(); } } } diff --git a/apps/frontend/public/icons/authentik.svg b/apps/frontend/public/icons/authentik.svg new file mode 100644 index 000000000..c839ddab9 --- /dev/null +++ b/apps/frontend/public/icons/authentik.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/frontend/src/app/layout.tsx b/apps/frontend/src/app/layout.tsx index 07ec93654..fc65bef53 100644 --- a/apps/frontend/src/app/layout.tsx +++ b/apps/frontend/src/app/layout.tsx @@ -39,6 +39,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!} frontEndUrl={process.env.FRONTEND_URL!} isGeneral={!!process.env.IS_GENERAL} + authentikOIDC={!!process.env.AUTHENTIK_OIDC} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} tolt={process.env.NEXT_PUBLIC_TOLT!} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index a0279e91d..980e1e416 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -9,6 +9,7 @@ import { useMemo, useState } from 'react'; import { classValidatorResolver } from '@hookform/resolvers/class-validator'; import { LoginUserDto } from '@gitroom/nestjs-libraries/dtos/auth/login.user.dto'; import { GithubProvider } from '@gitroom/frontend/components/auth/providers/github.provider'; +import { AuthentikProvider } from '@gitroom/frontend/components/auth/providers/authentik.provider'; import interClass from '@gitroom/react/helpers/inter.font'; import { GoogleProvider } from '@gitroom/frontend/components/auth/providers/google.provider'; import { useVariables } from '@gitroom/react/helpers/variable.context'; @@ -23,7 +24,7 @@ type Inputs = { export function Login() { const [loading, setLoading] = useState(false); - const { isGeneral, neynarClientId } = useVariables(); + const { isGeneral, neynarClientId, authentikOIDC } = useVariables(); const resolver = useMemo(() => { return classValidatorResolver(LoginUserDto); }, []); @@ -62,8 +63,9 @@ export function Login() { Sign In - - {!isGeneral ? ( + {isGeneral && authentikOIDC ? ( + + ) : !isGeneral ? ( ) : (
diff --git a/apps/frontend/src/components/auth/providers/authentik.provider.tsx b/apps/frontend/src/components/auth/providers/authentik.provider.tsx new file mode 100644 index 000000000..985825ad9 --- /dev/null +++ b/apps/frontend/src/components/auth/providers/authentik.provider.tsx @@ -0,0 +1,40 @@ +import { useCallback } from 'react'; +import Image from 'next/image'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import interClass from '@gitroom/react/helpers/inter.font'; + +export const AuthentikProvider = () => { + const fetch = useFetch(); + + const gotoLogin = useCallback(async () => { + try { + const response = await fetch('/auth/oauth/AUTHENTIK'); + if (!response.ok) { + throw new Error( + `Login link request failed with status ${response.status}` + ); + } + const link = await response.text(); + window.location.href = link; + } catch (error) { + console.error('Failed to get Authentik login link:', error); + } + }, []); + + return ( +
+
+ Authentik +
+
Sign in with Authentik
+
+ ); +}; diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index 800e3d8cb..01aae290d 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -44,7 +44,9 @@ export async function middleware(request: NextRequest) { ? '' : (url.indexOf('?') > -1 ? '&' : '?') + `provider=${(findIndex === 'settings' - ? 'github' + ? process.env.AUTHENTIK_OIDC + ? 'authentik' + : 'github' : findIndex ).toUpperCase()}`; return NextResponse.redirect( diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 4cbd162d6..515aeaa05 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -566,6 +566,7 @@ enum Provider { GITHUB GOOGLE FARCASTER + AUTHENTIK } enum Role { @@ -578,4 +579,4 @@ enum APPROVED_SUBMIT_FOR_ORDER { NO WAITING_CONFIRMATION YES -} \ No newline at end of file +} diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index 48cb85f5b..bf2ae9a33 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -5,9 +5,10 @@ import { createContext, FC, ReactNode, useContext, useEffect } from 'react'; interface VariableContextInterface { billingEnabled: boolean; isGeneral: boolean; + authentikOIDC: boolean; frontEndUrl: string; plontoKey: string; - storageProvider: 'local' | 'cloudflare', + storageProvider: 'local' | 'cloudflare'; backendUrl: string; discordUrl: string; uploadDirectory: string; @@ -20,6 +21,7 @@ interface VariableContextInterface { const VariableContext = createContext({ billingEnabled: false, isGeneral: true, + authentikOIDC: false, frontEndUrl: '', storageProvider: 'local', plontoKey: '', @@ -52,9 +54,9 @@ export const VariableContextComponent: FC< export const useVariables = () => { return useContext(VariableContext); -} +}; export const loadVars = () => { // @ts-ignore return window.vars as VariableContextInterface; -} +}; From 1a13d46d7de1636e3ab2dfb0ce75d7f81b8e78d1 Mon Sep 17 00:00:00 2001 From: DrummyFloyd Date: Fri, 31 Jan 2025 16:21:44 +0100 Subject: [PATCH 2/2] feat(oidc): use generic implementation --- .env.example | 21 ++++---- ...uthentik.provider.ts => oauth.provider.ts} | 51 ++++++++++++------- .../auth/providers/providers.factory.ts | 6 +-- apps/frontend/public/icons/authentik.svg | 1 - apps/frontend/public/icons/generic-oauth.svg | 9 ++++ apps/frontend/src/app/layout.tsx | 4 +- apps/frontend/src/components/auth/login.tsx | 8 +-- ...hentik.provider.tsx => oauth.provider.tsx} | 14 ++--- apps/frontend/src/middleware.ts | 4 +- .../src/database/prisma/schema.prisma | 2 +- .../src/helpers/variable.context.tsx | 8 ++- 11 files changed, 81 insertions(+), 47 deletions(-) rename apps/backend/src/services/auth/providers/{authentik.provider.ts => oauth.provider.ts} (54%) delete mode 100644 apps/frontend/public/icons/authentik.svg create mode 100644 apps/frontend/public/icons/generic-oauth.svg rename apps/frontend/src/components/auth/providers/{authentik.provider.tsx => oauth.provider.tsx} (65%) diff --git a/.env.example b/.env.example index fd7dd7433..f21a87398 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # Configuration reference: http://docs.postiz.com/configuration/reference -# === Required Settings +# === Required Settings DATABASE_URL="postgresql://postiz-user:postiz-password@localhost:5432/postiz-db-local" REDIS_URL="redis://localhost:6379" JWT_SECRET="random string for your JWT secret, make it long" @@ -21,7 +21,6 @@ CLOUDFLARE_BUCKETNAME="postiz" CLOUDFLARE_BUCKET_URL="https://QhcMSXQyPuMCRpSQcSYdEuTYgHeCXHbu.r2.cloudflarestorage.com/" CLOUDFLARE_REGION="auto" - # === Common optional Settings ## This is a dummy key, you must create your own from Resend. @@ -32,7 +31,7 @@ CLOUDFLARE_REGION="auto" #EMAIL_FROM_NAME="" # Where will social media icons be saved - local or cloudflare. -STORAGE_PROVIDER="local" +STORAGE_PROVIDER="local" # Your upload directory path if you host your files locally, otherwise Cloudflare will be used. #UPLOAD_DIRECTORY="" @@ -40,7 +39,6 @@ STORAGE_PROVIDER="local" # Your upload directory path if you host your files locally, otherwise Cloudflare will be used. #NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY="" - # Social Media API Settings X_API_KEY="" X_API_SECRET="" @@ -91,8 +89,13 @@ STRIPE_SIGNING_KEY_CONNECT="" # Developer Settings NX_ADD_PLUGINS=false IS_GENERAL="true" # required for now -AUTHENTIK_OIDC="false" -AUTHENTIK_URL="" -AUTHENTIK_CLIENT_ID="" -AUTHENTIK_CLIENT_SECRET="" -AUTHENTIK_SCOPE="" +NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME="Authentik" +NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL="https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png" +POSTIZ_GENERIC_OAUTH="false" +POSTIZ_OAUTH_URL="https://auth.example.com" +POSTIZ_OAUTH_AUTH_URL="https://auth.example.com/application/o/authorize" +POSTIZ_OAUTH_TOKEN_URL="https://auth.example.com/application/o/token" +POSTIZ_OAUTH_USERINFO_URL="https://authentik.example.com/application/o/userinfo" +POSTIZ_OAUTH_CLIENT_ID="" +POSTIZ_OAUTH_CLIENT_SECRET="" +# POSTIZ_OAUTH_SCOPE="openid profile email" # default values diff --git a/apps/backend/src/services/auth/providers/authentik.provider.ts b/apps/backend/src/services/auth/providers/oauth.provider.ts similarity index 54% rename from apps/backend/src/services/auth/providers/authentik.provider.ts rename to apps/backend/src/services/auth/providers/oauth.provider.ts index 88f1c1d1b..9ce6f2785 100644 --- a/apps/backend/src/services/auth/providers/authentik.provider.ts +++ b/apps/backend/src/services/auth/providers/oauth.provider.ts @@ -1,36 +1,51 @@ import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface'; -export class AuthentikProvider implements ProvidersInterface { +export class OauthProvider implements ProvidersInterface { + private readonly authUrl: string; private readonly baseUrl: string; private readonly clientId: string; private readonly clientSecret: string; private readonly frontendUrl: string; + private readonly tokenUrl: string; + private readonly userInfoUrl: string; constructor() { const { - AUTHENTIK_URL, - AUTHENTIK_CLIENT_ID, - AUTHENTIK_CLIENT_SECRET, + POSTIZ_OAUTH_AUTH_URL, + POSTIZ_OAUTH_CLIENT_ID, + POSTIZ_OAUTH_CLIENT_SECRET, + POSTIZ_OAUTH_TOKEN_URL, + POSTIZ_OAUTH_URL, + POSTIZ_OAUTH_USERINFO_URL, FRONTEND_URL, } = process.env; - if (!AUTHENTIK_URL) - throw new Error('AUTHENTIK_URL environment variable is not set'); - if (!AUTHENTIK_CLIENT_ID) - throw new Error('AUTHENTIK_CLIENT_ID environment variable is not set'); - if (!AUTHENTIK_CLIENT_SECRET) + if (!POSTIZ_OAUTH_USERINFO_URL) throw new Error( - 'AUTHENTIK_CLIENT_SECRET environment variable is not set' + 'POSTIZ_OAUTH_USERINFO_URL environment variable is not set' ); + if (!POSTIZ_OAUTH_URL) + throw new Error('POSTIZ_OAUTH_URL environment variable is not set'); + if (!POSTIZ_OAUTH_TOKEN_URL) + throw new Error('POSTIZ_OAUTH_TOKEN_URL environment variable is not set'); + if (!POSTIZ_OAUTH_CLIENT_ID) + throw new Error('POSTIZ_OAUTH_CLIENT_ID environment variable is not set'); + if (!POSTIZ_OAUTH_CLIENT_SECRET) + throw new Error( + 'POSTIZ_OAUTH_CLIENT_SECRET environment variable is not set' + ); + if (!POSTIZ_OAUTH_AUTH_URL) + throw new Error('POSTIZ_OAUTH_AUTH_URL environment variable is not set'); if (!FRONTEND_URL) throw new Error('FRONTEND_URL environment variable is not set'); - this.baseUrl = AUTHENTIK_URL.endsWith('/') - ? AUTHENTIK_URL.slice(0, -1) - : AUTHENTIK_URL; - this.clientId = AUTHENTIK_CLIENT_ID; - this.clientSecret = AUTHENTIK_CLIENT_SECRET; + this.authUrl = POSTIZ_OAUTH_AUTH_URL; + this.baseUrl = POSTIZ_OAUTH_URL; + this.clientId = POSTIZ_OAUTH_CLIENT_ID; + this.clientSecret = POSTIZ_OAUTH_CLIENT_SECRET; this.frontendUrl = FRONTEND_URL; + this.tokenUrl = POSTIZ_OAUTH_TOKEN_URL; + this.userInfoUrl = POSTIZ_OAUTH_USERINFO_URL; } generateLink(): string { @@ -41,11 +56,11 @@ export class AuthentikProvider implements ProvidersInterface { redirect_uri: `${this.frontendUrl}/settings`, }); - return `${this.baseUrl}/application/o/authorize/?${params.toString()}`; + return `${this.authUrl}/?${params.toString()}`; } async getToken(code: string): Promise { - const response = await fetch(`${this.baseUrl}/application/o/token/`, { + const response = await fetch(`${this.tokenUrl}/`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -70,7 +85,7 @@ export class AuthentikProvider implements ProvidersInterface { } async getUser(access_token: string): Promise<{ email: string; id: string }> { - const response = await fetch(`${this.baseUrl}/application/o/userinfo/`, { + const response = await fetch(`${this.userInfoUrl}/`, { headers: { Authorization: `Bearer ${access_token}`, Accept: 'application/json', diff --git a/apps/backend/src/services/auth/providers/providers.factory.ts b/apps/backend/src/services/auth/providers/providers.factory.ts index c2d628642..b73ada113 100644 --- a/apps/backend/src/services/auth/providers/providers.factory.ts +++ b/apps/backend/src/services/auth/providers/providers.factory.ts @@ -3,7 +3,7 @@ import { GithubProvider } from '@gitroom/backend/services/auth/providers/github. import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface'; import { GoogleProvider } from '@gitroom/backend/services/auth/providers/google.provider'; import { FarcasterProvider } from '@gitroom/backend/services/auth/providers/farcaster.provider'; -import { AuthentikProvider } from '@gitroom/backend/services/auth/providers/authentik.provider'; +import { OauthProvider } from '@gitroom/backend/services/auth/providers/oauth.provider'; export class ProvidersFactory { static loadProvider(provider: Provider): ProvidersInterface { @@ -14,8 +14,8 @@ export class ProvidersFactory { return new GoogleProvider(); case Provider.FARCASTER: return new FarcasterProvider(); - case Provider.AUTHENTIK: - return new AuthentikProvider(); + case Provider.GENERIC: + return new OauthProvider(); } } } diff --git a/apps/frontend/public/icons/authentik.svg b/apps/frontend/public/icons/authentik.svg deleted file mode 100644 index c839ddab9..000000000 --- a/apps/frontend/public/icons/authentik.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/frontend/public/icons/generic-oauth.svg b/apps/frontend/public/icons/generic-oauth.svg new file mode 100644 index 000000000..b06d14984 --- /dev/null +++ b/apps/frontend/public/icons/generic-oauth.svg @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/apps/frontend/src/app/layout.tsx b/apps/frontend/src/app/layout.tsx index fc65bef53..b232217e7 100644 --- a/apps/frontend/src/app/layout.tsx +++ b/apps/frontend/src/app/layout.tsx @@ -39,7 +39,9 @@ export default async function AppLayout({ children }: { children: ReactNode }) { discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!} frontEndUrl={process.env.FRONTEND_URL!} isGeneral={!!process.env.IS_GENERAL} - authentikOIDC={!!process.env.AUTHENTIK_OIDC} + genericOauth={!!process.env.POSTIZ_GENERIC_OAUTH} + oauthLogoUrl={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL!} + oauthDisplayName={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME!} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} tolt={process.env.NEXT_PUBLIC_TOLT!} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index 980e1e416..d268bdab1 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -9,7 +9,7 @@ import { useMemo, useState } from 'react'; import { classValidatorResolver } from '@hookform/resolvers/class-validator'; import { LoginUserDto } from '@gitroom/nestjs-libraries/dtos/auth/login.user.dto'; import { GithubProvider } from '@gitroom/frontend/components/auth/providers/github.provider'; -import { AuthentikProvider } from '@gitroom/frontend/components/auth/providers/authentik.provider'; +import { OauthProvider } from '@gitroom/frontend/components/auth/providers/oauth.provider'; import interClass from '@gitroom/react/helpers/inter.font'; import { GoogleProvider } from '@gitroom/frontend/components/auth/providers/google.provider'; import { useVariables } from '@gitroom/react/helpers/variable.context'; @@ -24,7 +24,7 @@ type Inputs = { export function Login() { const [loading, setLoading] = useState(false); - const { isGeneral, neynarClientId, authentikOIDC } = useVariables(); + const { isGeneral, neynarClientId, genericOauth } = useVariables(); const resolver = useMemo(() => { return classValidatorResolver(LoginUserDto); }, []); @@ -63,8 +63,8 @@ export function Login() { Sign In
- {isGeneral && authentikOIDC ? ( - + {isGeneral && genericOauth ? ( + ) : !isGeneral ? ( ) : ( diff --git a/apps/frontend/src/components/auth/providers/authentik.provider.tsx b/apps/frontend/src/components/auth/providers/oauth.provider.tsx similarity index 65% rename from apps/frontend/src/components/auth/providers/authentik.provider.tsx rename to apps/frontend/src/components/auth/providers/oauth.provider.tsx index 985825ad9..69b152103 100644 --- a/apps/frontend/src/components/auth/providers/authentik.provider.tsx +++ b/apps/frontend/src/components/auth/providers/oauth.provider.tsx @@ -2,13 +2,15 @@ import { useCallback } from 'react'; import Image from 'next/image'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import interClass from '@gitroom/react/helpers/inter.font'; +import { useVariables } from '@gitroom/react/helpers/variable.context'; -export const AuthentikProvider = () => { +export const OauthProvider = () => { const fetch = useFetch(); + const { oauthLogoUrl, oauthDisplayName } = useVariables(); const gotoLogin = useCallback(async () => { try { - const response = await fetch('/auth/oauth/AUTHENTIK'); + const response = await fetch('/auth/oauth/GENERIC'); if (!response.ok) { throw new Error( `Login link request failed with status ${response.status}` @@ -17,7 +19,7 @@ export const AuthentikProvider = () => { const link = await response.text(); window.location.href = link; } catch (error) { - console.error('Failed to get Authentik login link:', error); + console.error('Failed to get generic oauth login link:', error); } }, []); @@ -28,13 +30,13 @@ export const AuthentikProvider = () => { >
Authentik
-
Sign in with Authentik
+
Sign in with {oauthDisplayName || 'OAuth'}
); }; diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index 01aae290d..a7f7076d0 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -44,8 +44,8 @@ export async function middleware(request: NextRequest) { ? '' : (url.indexOf('?') > -1 ? '&' : '?') + `provider=${(findIndex === 'settings' - ? process.env.AUTHENTIK_OIDC - ? 'authentik' + ? process.env.POSTIZ_GENERIC_OAUTH + ? 'generic' : 'github' : findIndex ).toUpperCase()}`; diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 515aeaa05..e3d7c6b46 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -566,7 +566,7 @@ enum Provider { GITHUB GOOGLE FARCASTER - AUTHENTIK + GENERIC } enum Role { diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index bf2ae9a33..a6716c2e2 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -5,7 +5,9 @@ import { createContext, FC, ReactNode, useContext, useEffect } from 'react'; interface VariableContextInterface { billingEnabled: boolean; isGeneral: boolean; - authentikOIDC: boolean; + genericOauth: boolean; + oauthLogoUrl: string; + oauthDisplayName: string; frontEndUrl: string; plontoKey: string; storageProvider: 'local' | 'cloudflare'; @@ -21,7 +23,9 @@ interface VariableContextInterface { const VariableContext = createContext({ billingEnabled: false, isGeneral: true, - authentikOIDC: false, + genericOauth: false, + oauthLogoUrl: '', + oauthDisplayName: '', frontEndUrl: '', storageProvider: 'local', plontoKey: '',