diff --git a/docs/docs/providers/github.md b/docs/docs/providers/github.md index 5a4c73733a..ca4c288c8e 100644 --- a/docs/docs/providers/github.md +++ b/docs/docs/providers/github.md @@ -19,7 +19,7 @@ https://github.com/settings/apps The **GitHub Provider** comes with a set of default options: -- [GitHub Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/github.js) +- [GitHub Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/github.ts) You can override any of the options to suit your own use case. diff --git a/docs/versioned_docs/version-v3/providers/github.md b/docs/versioned_docs/version-v3/providers/github.md index b11693cfcb..08dabd3303 100644 --- a/docs/versioned_docs/version-v3/providers/github.md +++ b/docs/versioned_docs/version-v3/providers/github.md @@ -15,7 +15,7 @@ https://github.com/settings/apps The **Github Provider** comes with a set of default options: -- [Github Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/github.js) +- [Github Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/github.ts) You can override any of the options to suit your own use case. diff --git a/packages/next-auth/src/providers/github.js b/packages/next-auth/src/providers/github.js deleted file mode 100644 index b5a21fd710..0000000000 --- a/packages/next-auth/src/providers/github.js +++ /dev/null @@ -1,44 +0,0 @@ -/** @type {import(".").OAuthProvider} */ -export default function GitHub(options) { - return { - id: "github", - name: "GitHub", - type: "oauth", - authorization: "https://github.com/login/oauth/authorize?scope=read:user+user:email", - token: "https://github.com/login/oauth/access_token", - userinfo: { - url: "https://api.github.com/user", - async request({ client, tokens }) { - // Get base profile - const profile = await client.userinfo(tokens) - - // If user has email hidden, get their primary email from the GitHub API - if (!profile.email) { - const emails = await ( - await fetch("https://api.github.com/user/emails", { - headers: { Authorization: `token ${tokens.access_token}` }, - }) - ).json() - - if (emails?.length > 0) { - // Get primary email - profile.email = emails.find(email => email.primary)?.email; - // And if for some reason it doesn't exist, just use the first - if (!profile.email) profile.email = emails[0].email; - } - } - - return profile - }, - }, - profile(profile) { - return { - id: profile.id.toString(), - name: profile.name || profile.login, - email: profile.email, - image: profile.avatar_url, - } - }, - options, - } -} diff --git a/packages/next-auth/src/providers/github.ts b/packages/next-auth/src/providers/github.ts new file mode 100644 index 0000000000..d94f134528 --- /dev/null +++ b/packages/next-auth/src/providers/github.ts @@ -0,0 +1,109 @@ +import type { OAuthConfig, OAuthUserConfig } from "." + +/** + * Source https://docs.github.com/en/rest/users/users#get-the-authenticated-user + */ +export interface GithubProfile extends Record { + login: string + id: number + node_id: string + avatar_url: string + gravatar_id: string | null + url: string + html_url: string + followers_url: string + following_url: string + gists_url: string + starred_url: string + subscriptions_url: string + organizations_url: string + repos_url: string + events_url: string + received_events_url: string + type: string + site_admin: boolean + name: string | null + company: string | null + blog: string | null + location: string | null + email: string | null + hireable: boolean | null + bio: string | null + twitter_username?: string | null + public_repos: number + public_gists: number + followers: number + following: number + created_at: string + updated_at: string + private_gists?: number + total_private_repos?: number + owned_private_repos?: number + disk_usage?: number + suspended_at?: string | null + collaborators?: number + two_factor_authentication: boolean + plan?: { + collaborators: number + name: string + space: number + private_repos: number + } +} + +export interface GithubEmail extends Record { + email: string + primary: boolean + verified: boolean + visibility: string | null +} + +export default function Github

( + options: OAuthUserConfig

+): OAuthConfig

{ + return { + id: "github", + name: "GitHub", + type: "oauth", + authorization: { + url: "https://github.com/login/oauth/authorize", + params: { scope: "read:user+user:email" }, + }, + token: "https://github.com/login/oauth/access_token", + userinfo: { + url: "https://api.github.com/user", + async request({ client, tokens }) { + // Get base profile + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const profile = await client.userinfo(tokens.access_token!) + + // If user has email hidden, get their primary email from the GitHub API + if (!profile.email) { + const emails: GithubEmail[] = await ( + await fetch("https://api.github.com/user/emails", { + headers: { Authorization: `token ${tokens.access_token}` }, + }) + ).json() + + if (emails?.length > 0) { + // Get primary email + profile.email = emails.find((email) => email.primary)?.email + // And if for some reason it doesn't exist, just use the first + if (!profile.email) profile.email = emails[0].email + } + } + + return profile + }, + }, + profile(profile) { + return { + id: profile.id.toString(), + name: profile.name ?? profile.login, + email: profile.email, + image: profile.avatar_url, + } + }, + options, + } +}