diff --git a/src/config/auth.ts b/src/config/auth.ts index aa490a411e4e..0c64df8b7485 100644 --- a/src/config/auth.ts +++ b/src/config/auth.ts @@ -90,6 +90,11 @@ export const getAuthConfig = () => { ZITADEL_CLIENT_ID: z.string().optional(), ZITADEL_CLIENT_SECRET: z.string().optional(), ZITADEL_ISSUER: z.string().optional(), + + // LOGTO + LOGTO_CLIENT_ID: z.string().optional(), + LOGTO_CLIENT_SECRET: z.string().optional(), + LOGTO_ISSUER: z.string().optional(), }, runtimeEnv: { @@ -132,6 +137,11 @@ export const getAuthConfig = () => { ZITADEL_CLIENT_ID: process.env.ZITADEL_CLIENT_ID, ZITADEL_CLIENT_SECRET: process.env.ZITADEL_CLIENT_SECRET, ZITADEL_ISSUER: process.env.ZITADEL_ISSUER, + + // LOGTO + LOGTO_CLIENT_ID: process.env.LOGTO_CLIENT_ID, + LOGTO_CLIENT_SECRET: process.env.LOGTO_CLIENT_SECRET, + LOGTO_ISSUER: process.env.LOGTO_ISSUER, }, }); }; diff --git a/src/libs/next-auth/auth.config.ts b/src/libs/next-auth/auth.config.ts index 6dc8bd8a36ad..2ad9f7e8e1aa 100644 --- a/src/libs/next-auth/auth.config.ts +++ b/src/libs/next-auth/auth.config.ts @@ -7,7 +7,7 @@ import { ssoProviders } from './sso-providers'; export const initSSOProviders = () => { return authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH ? authEnv.NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => { - const validProvider = ssoProviders.find((item) => item.id === provider); + const validProvider = ssoProviders.find((item) => item.id === provider.trim()); if (validProvider) return validProvider.provider; diff --git a/src/libs/next-auth/sso-providers/index.ts b/src/libs/next-auth/sso-providers/index.ts index b3d05b177342..32ca6dffb4b7 100644 --- a/src/libs/next-auth/sso-providers/index.ts +++ b/src/libs/next-auth/sso-providers/index.ts @@ -3,6 +3,7 @@ import Authelia from './authelia'; import Authentik from './authentik'; import AzureAD from './azure-ad'; import Github from './github'; +import Logto from './logto'; import Zitadel from './zitadel'; -export const ssoProviders = [Auth0, Authentik, AzureAD, Github, Zitadel, Authelia]; +export const ssoProviders = [Auth0, Authentik, AzureAD, Github, Zitadel, Authelia, Logto]; diff --git a/src/libs/next-auth/sso-providers/logto.ts b/src/libs/next-auth/sso-providers/logto.ts new file mode 100644 index 000000000000..c2e4fde062d2 --- /dev/null +++ b/src/libs/next-auth/sso-providers/logto.ts @@ -0,0 +1,50 @@ +import { OIDCConfig, OIDCUserConfig } from '@auth/core/providers'; + +import { authEnv } from '@/config/auth'; + +import { CommonProviderConfig } from './sso.config'; + +interface LogtoProfile extends Record { + email: string; + id: string; + name?: string; + picture: string; + sub: string; + username: string; +} + +function LobeLogtoProvider(config: OIDCUserConfig): OIDCConfig { + return { + ...CommonProviderConfig, + ...config, + id: 'logto', + name: 'Logto', + profile(profile) { + // You can customize the user profile mapping here + return { + email: profile.email, + id: profile.sub, + image: profile.picture, + name: profile.name ?? profile.username, + providerAccountId: profile.sub, + }; + }, + type: 'oidc', + }; +} + +const provider = { + id: 'logto', + provider: LobeLogtoProvider({ + authorization: { + params: { scope: 'openid offline_access profile email' }, + }, + // You can get the issuer value from the Logto Application Details page, + // in the field "Issuer endpoint" + clientId: authEnv.LOGTO_CLIENT_ID, + clientSecret: authEnv.LOGTO_CLIENT_SECRET, + issuer: authEnv.LOGTO_ISSUER, + }), +}; + +export default provider;