diff --git a/.github/ISSUE_TEMPLATE/2_bug_provider.yml b/.github/ISSUE_TEMPLATE/2_bug_provider.yml index 4545ebbf3e..4f4585b3d2 100644 --- a/.github/ISSUE_TEMPLATE/2_bug_provider.yml +++ b/.github/ISSUE_TEMPLATE/2_bug_provider.yml @@ -63,6 +63,7 @@ body: - "Kinde" - "Line" - "LinkedIn" + - "Logto" - "Loops" - "Mailchimp" - "Mail.ru" diff --git a/docs/pages/data/manifest.json b/docs/pages/data/manifest.json index 50fb339f1c..3369f99ae3 100644 --- a/docs/pages/data/manifest.json +++ b/docs/pages/data/manifest.json @@ -84,6 +84,7 @@ "kinde": "Kinde", "line": "LINE", "linkedin": "LinkedIn", + "logto": "Logto", "mailchimp": "Mailchimp", "mailru": "Mail.ru", "mastodon": "Mastodon", @@ -141,6 +142,7 @@ "identity-server4", "keycloak", "kinde", + "logto", "mastodon", "mattermost", "nextcloud", diff --git a/docs/pages/getting-started/providers/logto.mdx b/docs/pages/getting-started/providers/logto.mdx new file mode 100644 index 0000000000..d22140e163 --- /dev/null +++ b/docs/pages/getting-started/providers/logto.mdx @@ -0,0 +1,79 @@ +import { Callout } from "nextra/components" +import { Code } from "@/components/Code" + + + +# Logto Provider + +## Resources + +- [Logto Auth.js quickstart](https://docs.logto.io/quick-starts/next-auth) +- [Integrate Logto in your application](https://docs.logto.io/integrate-logto/integrate-logto-into-your-application) + +## Setup + +### Callback URL + + + + + ```bash + https://example.com/api/auth/callback/logto + ``` + + + + + ```bash + https://example.com/auth/callback/logto + ``` + + + + +### Environment Variables + +``` +AUTH_LOGTO_ID +AUTH_LOGTO_SECRET +AUTH_LOGTO_ISSUER +``` + +### Configuration + + + + +```ts filename="/auth.ts" +import NextAuth from "next-auth" +import Logto from "next-auth/providers/logto" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [Logto], +}) +``` + + + + +```ts filename="/src/auth.ts" +import { SvelteKitAuth } from "@auth/sveltekit" +import Logto from "@auth/sveltekit/providers/logto" + +export const { handle, signIn, signOut } = SvelteKitAuth({ + providers: [Logto], +}) +``` + + + + +```ts filename="/src/app.ts" +import { ExpressAuth } from "@auth/express" +import Logto from "@auth/express/providers/logto" + +app.use("/auth/*", ExpressAuth({ providers: [Logto] })) +``` + + + diff --git a/docs/public/img/providers/logto.svg b/docs/public/img/providers/logto.svg new file mode 100644 index 0000000000..4e603b99df --- /dev/null +++ b/docs/public/img/providers/logto.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/packages/core/src/providers/logto.ts b/packages/core/src/providers/logto.ts new file mode 100644 index 0000000000..ed65f3f2dd --- /dev/null +++ b/packages/core/src/providers/logto.ts @@ -0,0 +1,122 @@ +/** + *
+ * + * Built-in sign in with Logto integration. + * + * + * + * + *
+ * + * @module providers/logto + */ + +import type { OIDCConfig, OIDCUserConfig } from "./index.js" + +/** The returned user profile from Logto when using the profile callback. [Reference](https://docs.logto.io/quick-starts/next-auth#scopes-and-claims). */ +export interface LogtoProfile { + /** The user's unique ID */ + sub: string + /** The user's name */ + name: string + /** The user's username */ + username: string + /** The user's picture */ + picture: string + /** The user's email */ + email: string + /** A boolean indicating if the user's email is verified */ + email_verified: boolean + /** The user's phone number */ + phone_number: string + /** A boolean indicating if the user's phone number is verified */ + phone_number_verified: boolean + /** The user's address */ + address: string + /** Custom fields */ + custom_data: object + /** The linked identities of the user */ + identities: object + /** The linked SSO identities of the user */ + sso_identities: object[] + /** The organization IDs the user belongs to */ + organizations: string[] + /** The organization data the user belongs to */ + organization_data: object[] + /** The organization roles the user belongs to with the format of : */ + organization_roles: string[] + /** The user's custom attributes */ + [claim: string]: unknown +} + +/** + * + * ### Setup + * + * #### Callback URL + * ``` + * https://example.com/api/auth/callback/logto + * ``` + * + * #### Configuration + * ```ts + * import { Auth } from "@auth/core" + * import Logto from "@auth/core/providers/logto" + * + * const request = new Request(origin) + * const response = await Auth(request, { + * providers: [ + * Logto({ + * clientId: LOGTO_ID, + * clientSecret: LOGTO_SECRET, + * issuer: LOGTO_ISSUER + * }), + * ], + * }) + * ``` + * + * + * ### Resources + * + * - [Logto Auth.js quickstart](https://docs.logto.io/quick-starts/next-auth) + * - [Integrate Logto in your application](https://docs.logto.io/integrate-logto/integrate-logto-into-your-application) + * + * ### Notes + * + * The Logto provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/logto.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers). + * + * :::info + * By default, Auth.js assumes that the Logto provider is based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec + * ::: + * + * ## Help + * + * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue). + * + * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from + * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec, + * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions). + */ +export default function Logto( + options: OIDCUserConfig +): OIDCConfig { + return { + id: "logto", + name: "Logto", + type: "oidc", + authorization: { + params: { + scope: "offline_access openid email profile", + }, + }, + profile(profile) { + return { + id: profile.sub, + name: profile.name ?? profile.username, + email: profile.email, + image: profile.picture, + } + }, + options, + } +}