Skip to content

Commit 0a629d1

Browse files
committed
feat(provider): add ThunderID provider
1 parent 7b35fc3 commit 0a629d1

6 files changed

Lines changed: 414 additions & 1 deletion

File tree

apps/dev/nextjs/.env.local.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ AUTH_KEYCLOAK_ISSUER=
4646
AUTH_LINE_ID=
4747
AUTH_LINE_SECRET=
4848

49+
AUTH_THUNDERID_ID=
50+
AUTH_THUNDERID_SECRET=
51+
AUTH_THUNDERID_ISSUER=
52+
4953
AUTH_TRAKT_ID=
5054
AUTH_TRAKT_SECRET=
5155

docs/pages/data/manifest.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"slack": "Slack",
110110
"spotify": "Spotify",
111111
"strava": "Strava",
112+
"thunderid": "ThunderID",
112113
"tiktok": "TikTok",
113114
"todoist": "Todoist",
114115
"trakt": "Trakt",
@@ -154,6 +155,7 @@
154155
"ory-hydra",
155156
"osso",
156157
"passage",
157-
"ping-id"
158+
"ping-id",
159+
"thunderid"
158160
]
159161
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
title: ThunderID
3+
---
4+
5+
import { Callout } from "nextra/components"
6+
import { Code } from "@/components/Code"
7+
8+
<img align="right" src="/img/providers/thunderid.svg" width="64" height="64" />
9+
10+
# ThunderID Provider
11+
12+
## Resources
13+
14+
- [ThunderID Documentation](https://thunderid.dev/docs/next/)
15+
16+
## Setup
17+
18+
### Callback URL
19+
20+
<Code>
21+
<Code.Next>
22+
23+
```bash
24+
https://example.com/api/auth/callback/thunderid
25+
```
26+
27+
</Code.Next>
28+
<Code.Qwik>
29+
30+
```bash
31+
https://example.com/auth/callback/thunderid
32+
```
33+
34+
</Code.Qwik>
35+
<Code.Svelte>
36+
37+
```bash
38+
https://example.com/auth/callback/thunderid
39+
```
40+
41+
</Code.Svelte>
42+
</Code>
43+
44+
### Environment Variables
45+
46+
```
47+
AUTH_THUNDERID_ID
48+
AUTH_THUNDERID_SECRET
49+
AUTH_THUNDERID_ISSUER
50+
```
51+
52+
### Configuration
53+
54+
1. Get ThunderID installed on your environment. (via `npx thunderid` or any [other option](https://thunderid.dev/docs/next/guides/getting-started/get-thunderid/))
55+
2. Go to the **ThunderID Console** at `https://{THUNDERID_HOST}:{THUNDERID_PORT}/console`
56+
3. Create an application with the **Next.js** template
57+
> ⚠️ IMPORTANT: Make sure to copy the **Client Secret** at the end of the wizard since it will not be prompted again
58+
4. In the **General** tab, **Access** section → **Authorized redirect URIs**, add:
59+
- Development: `http://localhost:3000/api/auth/callback/thunderid`
60+
- Production: `https://{YOUR_DOMAIN}/api/auth/callback/thunderid`
61+
62+
Then, add the values to your environment variables.
63+
64+
<Code>
65+
<Code.Next>
66+
67+
```ts filename="/auth.ts"
68+
import NextAuth from "next-auth"
69+
import ThunderID from "next-auth/providers/thunderid"
70+
71+
export const { handlers, auth, signIn, signOut } = NextAuth({
72+
providers: [ThunderID],
73+
})
74+
```
75+
76+
</Code.Next>
77+
<Code.Qwik>
78+
79+
```ts filename="/src/routes/plugin@auth.ts"
80+
import { QwikAuth$ } from "@auth/qwik"
81+
import ThunderID from "@auth/qwik/providers/thunderid"
82+
83+
export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
84+
() => ({
85+
providers: [ThunderID],
86+
})
87+
)
88+
```
89+
90+
</Code.Qwik>
91+
<Code.Svelte>
92+
93+
```ts filename="/src/auth.ts"
94+
import { SvelteKitAuth } from "@auth/sveltekit"
95+
import ThunderID from "@auth/sveltekit/providers/thunderid"
96+
97+
export const { handle, signIn, signOut } = SvelteKitAuth({
98+
providers: [ThunderID],
99+
})
100+
```
101+
102+
</Code.Svelte>
103+
<Code.Express>
104+
105+
```ts filename="/src/app.ts"
106+
import { ExpressAuth } from "@auth/express"
107+
import ThunderID from "@auth/express/providers/thunderid"
108+
109+
app.use("/auth/*", ExpressAuth({ providers: [ThunderID] }))
110+
```
111+
112+
</Code.Express>
113+
</Code>
Lines changed: 8 additions & 0 deletions
Loading
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* <div class="provider" style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
3+
* <span style={{fontSize: "1.35rem" }}>
4+
* Built-in sign in with <b>ThunderID</b> integration.
5+
* </span>
6+
* <a href="https://thunderid.dev/" style={{backgroundColor: "#ECEFF1", padding: "12px", borderRadius: "100%" }}>
7+
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/thunderid.svg" width="24"/>
8+
* </a>
9+
* </div>
10+
*
11+
* @module providers/thunderid
12+
*/
13+
14+
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
15+
16+
/** The returned user profile from ThunderID when using the profile callback. */
17+
export interface ThunderIDProfile extends Record<string, any> {
18+
/** The user ThunderID account ID (subject identifier) */
19+
sub: string
20+
21+
// Standard OIDC `profile` scope claims
22+
/** The user's full name */
23+
name?: string
24+
/** The user's given (first) name */
25+
given_name?: string
26+
/** The user's family (last) name */
27+
family_name?: string
28+
/** The user's middle name */
29+
middle_name?: string
30+
/** The user's nickname */
31+
nickname?: string
32+
/** The user's preferred username */
33+
preferred_username?: string
34+
/** URL of the user's profile page */
35+
profile?: string
36+
/** URL of the user's profile picture */
37+
picture?: string
38+
/** URL of the user's website */
39+
website?: string
40+
/** The user's gender */
41+
gender?: string
42+
/** The user's birthdate (YYYY-MM-DD) */
43+
birthdate?: string
44+
/** The user's time zone (IANA timezone string) */
45+
zoneinfo?: string
46+
/** The user's locale (BCP 47 language tag) */
47+
locale?: string
48+
/** Unix timestamp of the last profile update */
49+
updated_at?: number
50+
51+
// Standard OIDC `email` scope claims
52+
/** The user's email address */
53+
email?: string
54+
/** Whether the email address has been verified */
55+
email_verified?: boolean
56+
57+
// Standard OIDC `phone` scope claims
58+
/** The user's phone number */
59+
phone_number?: string
60+
/** Whether the phone number has been verified */
61+
phone_number_verified?: boolean
62+
63+
// Standard OIDC `address` scope claims
64+
/** The user's postal address */
65+
address?: string | Record<string, string>
66+
67+
// ThunderID custom claims
68+
/** The user type as configured in the ThunderID system */
69+
userType?: string
70+
/** Organization unit ID the user belongs to */
71+
ouId?: string
72+
/** Organization unit name */
73+
ouName?: string
74+
/** Organization unit handle */
75+
ouHandle?: string
76+
/** Roles assigned to the user (returned with the `roles` scope) */
77+
roles?: string[]
78+
/** Groups the user belongs to (requires explicit configuration) */
79+
groups?: string[]
80+
}
81+
82+
/**
83+
*
84+
* ### Setup
85+
*
86+
* #### Callback URL
87+
* ```
88+
* https://example.com/api/auth/callback/thunderid
89+
* ```
90+
*
91+
* #### Configuration
92+
*```ts
93+
* import { Auth } from "@auth/core"
94+
* import ThunderID from "@auth/core/providers/thunderid"
95+
*
96+
* const request = new Request(origin)
97+
* const response = await Auth(request, {
98+
* providers: [
99+
* ThunderID({
100+
* clientId: AUTH_THUNDERID_ID,
101+
* clientSecret: AUTH_THUNDERID_SECRET,
102+
* issuer: AUTH_THUNDERID_ISSUER,
103+
* }),
104+
* ],
105+
* })
106+
* ```
107+
*
108+
* ### Configuring ThunderID
109+
*
110+
* 1. Get ThunderID installed on your environment (via `npx thunderid` or any [other option](https://thunderid.dev/docs/next/guides/getting-started/get-thunderid/))
111+
* 2. Go to the **ThunderID Console** at `https://{THUNDERID_HOST}:{THUNDERID_PORT}/console`
112+
* 3. Create an application with the **Next.js** template
113+
* > **Important:** Copy the **Client Secret** at the end of the wizard — it will not be shown again
114+
* 4. In the **General** tab, **Access** section → **Authorized redirect URIs**, add:
115+
* - Development: `http://localhost:3000/api/auth/callback/thunderid`
116+
* - Production: `https://{YOUR_DOMAIN}/api/auth/callback/thunderid`
117+
*
118+
* Then, create a `.env.local` file in the project root and add the following entries:
119+
*
120+
* ```
121+
* AUTH_THUNDERID_ID="Your Client ID here"
122+
* AUTH_THUNDERID_SECRET="Your Client Secret here"
123+
* AUTH_THUNDERID_ISSUER="Your ThunderID issuer URL here"
124+
* ```
125+
*
126+
* ### Resources
127+
*
128+
* - [ThunderID Documentation](https://thunderid.dev/docs/next/)
129+
* - [OAuth 2.0 / OpenID Connect Documentation](https://tools.ietf.org/html/rfc6749)
130+
* - [Learn more about OAuth](https://authjs.dev/concepts/oauth)
131+
*
132+
* ### Notes
133+
*
134+
* The ThunderID provider comes with a [default configuration](#configuration). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers).
135+
*
136+
* :::info
137+
* By default, Auth.js assumes that the ThunderID provider is based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) and [OpenID Connect](https://openid.net/connect/) specs
138+
* :::
139+
*
140+
* ## Help
141+
*
142+
* If you think you found a bug in the default configuration, you can [open an issue](https://github.com/nextauthjs/next-auth/issues/new?title=ThunderID%20Provider).
143+
*
144+
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
145+
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
146+
* we might not pursue a resolution. You can ask for more help in [GitHub Discussions](https://github.com/nextauthjs/next-auth/discussions).
147+
*/
148+
export default function ThunderID(
149+
config: OIDCUserConfig<ThunderIDProfile>
150+
): OIDCConfig<ThunderIDProfile> {
151+
const issuer = config?.issuer?.replace(/\/$/, "")
152+
153+
return {
154+
id: "thunderid",
155+
name: "ThunderID",
156+
type: "oidc",
157+
wellKnown: `${issuer}/.well-known/openid-configuration`,
158+
style: {
159+
bg: "#3688FF",
160+
text: "#ffffff",
161+
},
162+
options: config,
163+
client: { token_endpoint_auth_method: "client_secret_basic" },
164+
profile(profile) {
165+
return {
166+
id: profile.sub,
167+
name:
168+
profile.name ??
169+
([profile.given_name, profile.family_name]
170+
.filter(Boolean)
171+
.join(" ") ||
172+
null),
173+
email: profile.email ?? null,
174+
image: profile.picture ?? null,
175+
}
176+
},
177+
}
178+
}

0 commit comments

Comments
 (0)