Skip to content

Commit a5e0db4

Browse files
ThangHuuVuharrison-broadbentHarrison Broadbent
authored
feat(providers): add Notion provider (#6567)
* add notion provider along with logo and styles " * adjust notion documentation * update issue template with Notion provider * update docs and provider with code from TomYeoman * feat: move Notion provider to core * get it working --------- Co-authored-by: Harrison Broadbent <[email protected]> Co-authored-by: Harrison Broadbent <[email protected]> Co-authored-by: Thang Vu <[email protected]>
1 parent 334e233 commit a5e0db4

File tree

5 files changed

+179
-1
lines changed

5 files changed

+179
-1
lines changed

.github/ISSUE_TEMPLATE/2_bug_provider.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ body:
5858
- "Medium"
5959
- "Naver"
6060
- "Netlify"
61+
- "Notion"
6162
- "Okta"
6263
- "OneLogin"
6364
- "Osso"

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ KEYCLOAK_ID=
2121
KEYCLOAK_SECRET=
2222
KEYCLOAK_ISSUER=
2323

24+
NOTION_ID=
25+
NOTION_SECRET=
26+
NOTION_REDIRECT_URI=
27+
2428
IDS4_ID=
2529
IDS4_SECRET=
2630
IDS4_ISSUER=

apps/dev/nextjs/pages/api/auth/[...nextauth].ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Instagram from "@auth/core/providers/instagram"
2424
import Line from "@auth/core/providers/line"
2525
import LinkedIn from "@auth/core/providers/linkedin"
2626
import Mailchimp from "@auth/core/providers/mailchimp"
27+
import Notion from "@auth/core/providers/notion"
2728
// import Okta from "@auth/core/providers/okta"
2829
import Osu from "@auth/core/providers/osu"
2930
import Patreon from "@auth/core/providers/patreon"
@@ -69,7 +70,7 @@ import WorkOS from "@auth/core/providers/workos"
6970

7071
export const authConfig: AuthConfig = {
7172
// adapter,
72-
// debug: process.env.NODE_ENV !== "production",
73+
debug: process.env.NODE_ENV !== "production",
7374
theme: {
7475
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
7576
brandColor: "#1786fb",
@@ -107,6 +108,7 @@ export const authConfig: AuthConfig = {
107108
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
108109
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
109110
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_SECRET }),
111+
Notion({ clientId: process.env.NOTION_ID, clientSecret: process.env.NOTION_SECRET, redirectUri: process.env.NOTION_REDIRECT_URI }),
110112
// Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
111113
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
112114
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),

docs/static/img/providers/notion.svg

Lines changed: 5 additions & 0 deletions
Loading

packages/core/src/providers/notion.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/**
2+
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
3+
* <span>Built-in <b>Notion</b> integration.</span>
4+
* <a href="https://notion.so">
5+
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/notion.svg" height="48" width="48"/>
6+
* </a>
7+
* </div>
8+
*
9+
* ---
10+
* @module providers/notion
11+
*/
12+
13+
import type { OAuthConfig, OAuthUserConfig } from "."
14+
15+
export interface Person extends Record<string, any> {
16+
email: string
17+
}
18+
19+
// https://developers.notion.com/reference/user
20+
export interface User extends Record<string, any> {
21+
object: "user" | "bot"
22+
id: string
23+
type: string
24+
name: string
25+
avatar_url: null | string
26+
person: Person
27+
owner?: {
28+
type: "workspace" | "user"
29+
workspace: string
30+
}
31+
workspace_name?: string | null
32+
}
33+
34+
export interface Owner {
35+
type: string
36+
user: User
37+
}
38+
39+
// Notion responds with an access_token + some additional information, which we define here
40+
// More info - https://developers.notion.com/docs/authorization#step-4-notion-responds-with-an-access_token-and-some-additional-information
41+
export interface NotionProfile extends Record<string, any> {
42+
access_token: string
43+
bot_id: string
44+
duplicated_template_id: string
45+
owner?: Owner
46+
workspace_icon: string
47+
workspace_id: number
48+
workspace_name: string
49+
}
50+
51+
// Any config required that isn't part of the `OAuthUserConfig` spec should belong here
52+
// For example, we must pass a `redirectUri` to the Notion API when requesting tokens, therefore we add it here
53+
interface AdditionalConfig {
54+
redirectUri: string
55+
}
56+
57+
const NOTION_HOST = "https://api.notion.com"
58+
const NOTION_API_VERSION = "2022-06-28"
59+
60+
/**
61+
* Add Notion login to your page.
62+
*
63+
* ## Example
64+
*
65+
* ```ts
66+
* import { Auth } from "@auth/core"
67+
* import Notion from "@auth/core/providers/notion"
68+
*
69+
* const request = new Request("https://example.com")
70+
* const response = await Auth(request, {
71+
* providers: [Notion({ clientId: "", clientSecret: "", redirectUri: "" })],
72+
* })
73+
* ```
74+
*
75+
* ---
76+
*
77+
* ## Resources
78+
* - [Notion Docs](https://developers.notion.com/docs)
79+
* - [Notion Authorization Docs](https://developers.notion.com/docs/authorization)
80+
* - [Notion Integrations](https://www.notion.so/my-integrations)
81+
*
82+
* ---
83+
*
84+
* ## Notes
85+
* You need to select "Public Integration" on the configuration page to get an `oauth_id` and `oauth_secret`. Private integrations do not provide these details.
86+
* You must provide a `clientId` and `clientSecret` to use this provider, as-well as a redirect URI (due to this being required by Notion endpoint to fetch tokens).
87+
*
88+
* :::tip
89+
*
90+
* The Notion provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/notion.ts).
91+
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
92+
*
93+
* :::
94+
*
95+
* :::info **Disclaimer**
96+
*
97+
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
98+
*
99+
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
100+
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
101+
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
102+
*
103+
* :::
104+
*/
105+
export default function NotionProvider<P extends NotionProfile>(
106+
options: OAuthUserConfig<P> & AdditionalConfig
107+
): OAuthConfig<P> {
108+
return {
109+
id: "notion",
110+
name: "Notion",
111+
type: "oauth",
112+
token: {
113+
url: `${NOTION_HOST}/v1/oauth/token`,
114+
},
115+
userinfo: {
116+
url: `${NOTION_HOST}/v1/users`,
117+
118+
// The result of this method will be the input to the `profile` callback.
119+
// We use a custom request handler, since we need to do things such as pass the "Notion-Version" header
120+
// More info: https://next-auth.js.org/configuration/providers/oauth
121+
async request(context) {
122+
const profile = await fetch(`${NOTION_HOST}/v1/users/me`, {
123+
headers: {
124+
Authorization: `Bearer ${context.tokens.access_token}`,
125+
"Notion-Version": NOTION_API_VERSION,
126+
},
127+
})
128+
129+
const {
130+
bot: {
131+
owner: { user },
132+
},
133+
} = await profile.json()
134+
135+
return user
136+
},
137+
},
138+
authorization: {
139+
params: {
140+
client_id: options.clientId,
141+
response_type: "code",
142+
owner: "user",
143+
redirect_uri: options.redirectUri,
144+
},
145+
url: `${NOTION_HOST}/v1/oauth/authorize`,
146+
},
147+
148+
async profile(profile, tokens) {
149+
return {
150+
id: profile.id,
151+
name: profile.name,
152+
email: profile.person.email,
153+
image: profile.avatar_url,
154+
}
155+
},
156+
style: {
157+
logo: "/notion.svg",
158+
logoDark: "/notion.svg",
159+
bg: "#fff",
160+
text: "#000",
161+
bgDark: "#fff",
162+
textDark: "#000",
163+
},
164+
options,
165+
}
166+
}

0 commit comments

Comments
 (0)