Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve OAuth provider configuration #2411

Merged
merged 40 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
df7cecb
fix(oauth): parse profile if returned as string
balazsorban44 Jul 20, 2021
f5ce7f5
refactor(oauth): assume 2.* as default spec
balazsorban44 Jul 20, 2021
01eb086
fix(oauth): remove async from legacy client
balazsorban44 Jul 20, 2021
d749d9f
feat: authorizationUrl -> authorization
balazsorban44 Jul 20, 2021
269afe7
refactor: simplify providers
balazsorban44 Jul 20, 2021
c034d8b
fix: fix OAuth 1 signin
balazsorban44 Jul 20, 2021
1b5e79a
chore: remove non-spec OAuth 1 params
balazsorban44 Jul 20, 2021
a46c258
chore: hint type for provider
balazsorban44 Jul 22, 2021
224e0aa
refactor: domain -> issuer
balazsorban44 Jul 22, 2021
d997e57
feat: add token option
balazsorban44 Jul 22, 2021
9c91134
feat: add jwks_uri option
balazsorban44 Jul 22, 2021
b8110c7
chore: update dev app
balazsorban44 Jul 22, 2021
a86140e
refactor: accessTokenUrl -> token, comply to OIDC
balazsorban44 Jul 22, 2021
328ba9c
chore: add Twitch to dev app
balazsorban44 Aug 1, 2021
38142a2
feat: add defaultProfile callback
balazsorban44 Aug 1, 2021
811489f
feat: add .well-known support
balazsorban44 Aug 1, 2021
eb8c392
refactor: use .well-known for Auth0 and IDS4
balazsorban44 Aug 1, 2021
309ef6a
fix: call client async, make auth params optional
balazsorban44 Aug 1, 2021
684412d
feat: add optional `token.request` callback
balazsorban44 Aug 1, 2021
1f3e939
test: temporarily disable tests in github actions
balazsorban44 Aug 1, 2021
f23e6eb
chore: remove old provider options
balazsorban44 Aug 1, 2021
8d5faa5
feat: pass extra info to `userinfo.request`
balazsorban44 Aug 1, 2021
1098374
refactor: rename `profileUrl` to `userinfo`
balazsorban44 Aug 1, 2021
7ec9130
chore: remove `console.log`
balazsorban44 Aug 1, 2021
453197f
docs: fix parsing issue
balazsorban44 Aug 2, 2021
72e52dd
chore: add provider to dev app
balazsorban44 Aug 2, 2021
9f86d1c
feat: pass whole `client` to endpoint requests
balazsorban44 Aug 2, 2021
109489a
fix: update some of the OAuth providers
balazsorban44 Aug 2, 2021
d9749f9
docs(ts): document OAuthConfig options
balazsorban44 Aug 2, 2021
2a16cc0
feat: allow `params` on `userinfo` and `tokens`
balazsorban44 Aug 2, 2021
17b89b1
feat: remove Basecamp provider
balazsorban44 Aug 4, 2021
531654d
chore: fix comment
balazsorban44 Aug 4, 2021
b881514
feat: deep merge user options with default ones
balazsorban44 Aug 4, 2021
cdf93c3
fix: make some options optional
balazsorban44 Aug 4, 2021
bf0af5e
test: update provider related tests
balazsorban44 Aug 4, 2021
f3e6de5
ci: re-enable tests
balazsorban44 Aug 4, 2021
920815d
fix: ts lint errors
balazsorban44 Aug 4, 2021
61ae394
docs(ts): explain options property
balazsorban44 Aug 4, 2021
c0311cf
chore: remove `jwks_uri` in favour of `wellKnown`
balazsorban44 Aug 4, 2021
67fa328
chore: remove Basecamp from dev app
balazsorban44 Aug 4, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ jobs:
- name: Build
run: npm run build
- name: Run tests
run: npm test -- --coverage --verbose && npm run test:types
run: "echo Re-enable tests..."
# run: npm test -- --coverage --verbose && npm run test:types
- name: Coverage
uses: codecov/codecov-action@v1
with:
Expand Down
9 changes: 8 additions & 1 deletion app/.env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ NEXTAUTH_URL=http://localhost:3000
SECRET=

AUTH0_ID=
AUTH0_DOMAIN=
AUTH0_SECRET=
AUTH0_ISSUER=

IDS4_ID=
IDS4_SECRET=
IDS4_ISSUER=

GITHUB_ID=
GITHUB_SECRET=

TWITCH_ID=
TWITCH_SECRET=

TWITTER_ID=
TWITTER_SECRET=

Expand Down
121 changes: 83 additions & 38 deletions app/pages/api/auth/[...nextauth].js
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,28 @@ import GitHubProvider from "next-auth/providers/github"
import Auth0Provider from "next-auth/providers/auth0"
import TwitterProvider from "next-auth/providers/twitter"
import CredentialsProvider from "next-auth/providers/credentials"
import IDS4Provider from "next-auth/providers/identity-server4"
import Twitch from "next-auth/providers/twitch"
import GoogleProvider from "next-auth/providers/google"
import FacebookProvider from "next-auth/providers/facebook"
import BasecampProvider from "next-auth/providers/basecamp"
import FoursquareProvider from "next-auth/providers/foursquare"
// import FreshbooksProvider from "next-auth/providers/freshbooks"
import GitlabProvider from "next-auth/providers/gitlab"
import InstagramProvider from "next-auth/providers/instagram"
import LineProvider from "next-auth/providers/line"
import LinkedInProvider from "next-auth/providers/linkedin"
import MailchimpProvider from "next-auth/providers/mailchimp"
import DiscordProvider from "next-auth/providers/discord"

export default NextAuth({
// Used to debug https://github.com/nextauthjs/next-auth/issues/1664
// cookies: {
// csrfToken: {
// name: 'next-auth.csrf-token',
// options: {
// httpOnly: true,
// sameSite: 'none',
// path: '/',
// secure: true
// }
// },
// pkceCodeVerifier: {
// name: 'next-auth.pkce.code_verifier',
// options: {
// httpOnly: true,
// sameSite: 'none',
// path: '/',
// secure: true
// }
// }
// },
providers: [
// E-mail
EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
GitHubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
domain: process.env.AUTH0_DOMAIN,
checks: ["pkce", "state"],
// params: {
// response_mode: "form_post",
// },
}),
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
// Credentials
CredentialsProvider({
name: "Credentials",
credentials: {
Expand All @@ -66,6 +43,74 @@ export default NextAuth({
return null
},
}),
// OAuth 1
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
// OAuth 2 / OIDC
GitHubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
}),
Twitch({
clientId: process.env.TWITCH_ID,
clientSecret: process.env.TWITCH_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
FacebookProvider({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
}),
BasecampProvider({
clientId: process.env.BASECAMP_ID,
clientSecret: process.env.BASECAMP_SECRET,
}),
FoursquareProvider({
clientId: process.env.FOURSQUARE_ID,
clientSecret: process.env.FOURSQUARE_SECRET,
}),
// FreshbooksProvider({
// clientId: process.env.FRESHBOOKS_ID,
// clientSecret: process.env.FRESHBOOKS_SECRET,
// }),
GitlabProvider({
clientId: process.env.GITLAB_ID,
clientSecret: process.env.GITLAB_SECRET,
}),
InstagramProvider({
clientId: process.env.INSTAGRAM_ID,
clientSecret: process.env.INSTAGRAM_SECRET,
}),
LineProvider({
clientId: process.env.LINE_ID,
clientSecret: process.env.LINE_SECRET,
}),
LinkedInProvider({
clientId: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
}),
MailchimpProvider({
clientId: process.env.MAILCHIMP_ID,
clientSecret: process.env.MAILCHIMP_SECRET,
}),
IDS4Provider({
clientId: process.env.IDS4_ID,
clientSecret: process.env.IDS4_SECRET,
issuer: process.env.IDS4_ISSUER,
}),
DiscordProvider({
clientId: process.env.DISCORD_ID,
clientSecret: process.env.DISCORD_SECRET,
}),
],
jwt: {
encryption: true,
Expand Down
29 changes: 14 additions & 15 deletions src/providers/42.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
export default function FortyTwo(options) {
return {
id: '42-school',
name: '42 School',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://api.intra.42.fr/oauth/token',
authorizationUrl:
'https://api.intra.42.fr/oauth/authorize?response_type=code',
profileUrl: 'https://api.intra.42.fr/v2/me',
profile: (profile) => ({
id: profile.id,
email: profile.email,
image: profile.image_url,
name: profile.usual_full_name,
}),
id: "42-school",
name: "42 School",
type: "oauth",
authorization: "https://api.intra.42.fr/oauth/authorize",
token: "https://api.intra.42.fr/oauth/token",
userinfo: "https://api.intra.42.fr/v2/me",
profile(profile) {
return {
id: profile.id,
name: profile.usual_full_name,
email: profile.email,
image: profile.image_url,
}
},
...options,
}
}
40 changes: 21 additions & 19 deletions src/providers/apple.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,33 @@ export default function Apple(options) {
id: "apple",
name: "Apple",
type: "oauth",
version: "2.0",
scope: "name email",
params: { grant_type: "authorization_code" },
accessTokenUrl: "https://appleid.apple.com/auth/token",
authorizationUrl:
"https://appleid.apple.com/auth/authorize?response_type=code&id_token&response_mode=form_post",
profileUrl: null,
idToken: true,
authorization: {
url: "https://appleid.apple.com/auth/authorize",
params: {
scope: "name email",
response_type: "code",
id_token: "",
response_mode: "form_post",
},
},
token: {
url: "https://appleid.apple.com/auth/token",
idToken: true,
},
jwks_endpoint: "https://appleid.apple.com/auth/keys",
profile(profile) {
// The name of the user will only return on first login
// The name of the user will only be returned on first login
const name = profile.user
? profile.user.name.firstName + " " + profile.user.name.lastName
: null

return {
id: profile.sub,
name:
profile.user != null
? profile.user.name.firstName + " " + profile.user.name.lastName
: null,
name,
email: profile.email,
image: null,
}
},
clientId: null,
clientSecret: {
teamId: null,
privateKey: null,
keyId: null,
},
checks: ["none"], // REVIEW: Apple does not support state, as far as I know. Can we use "pkce" then?
...options,
}
Expand Down
15 changes: 8 additions & 7 deletions src/providers/atlassian.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ export default function Atlassian(options) {
id: "atlassian",
name: "Atlassian",
type: "oauth",
version: "2.0",
params: {
grant_type: "authorization_code",
authorization: {
url: "https://auth.atlassian.com/oauth/authorize",
params: {
audience: "api.atlassian.com",
prompt: "consent",
},
},
accessTokenUrl: "https://auth.atlassian.com/oauth/token",
authorizationUrl:
"https://auth.atlassian.com/authorize?audience=api.atlassian.com&response_type=code&prompt=consent",
profileUrl: "https://api.atlassian.com/me",
token: "https://auth.atlassian.com/oauth/token",
userinfo: "https://api.atlassian.com/me",
profile(profile) {
return {
id: profile.account_id,
Expand Down
11 changes: 5 additions & 6 deletions src/providers/auth0.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/** @type {import("types/providers").OAuthProvider} */
export default function Auth0(options) {
return {
id: "auth0",
name: "Auth0",
wellKnown: `${options.issuer}/.well-known/openid-configuration`,
type: "oauth",
version: "2.0",
params: { grant_type: "authorization_code" },
scope: "openid email profile",
accessTokenUrl: `https://${options.domain}/oauth/token`,
authorizationUrl: `https://${options.domain}/authorize?response_type=code`,
profileUrl: `https://${options.domain}/userinfo`,
authorization: { params: { scope: "openid email profile" } },
checks: ["pkce", "state"],
idToken: true,
profile(profile) {
return {
id: profile.sub,
Expand Down
30 changes: 16 additions & 14 deletions src/providers/azure-ad-b2c.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
export default function AzureADB2C(options) {
const { tenantName, primaryUserFlow } = options
const authorizeUrl = `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${primaryUserFlow}/oauth2/v2.0/authorize`
const tokenUrl = `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${primaryUserFlow}/oauth2/v2.0/token`

return {
id: "azure-ad-b2c",
name: "Azure Active Directory B2C",
type: "oauth",
version: "2.0",
params: {
grant_type: "authorization_code",
authorization: {
url: `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${primaryUserFlow}/oauth2/v2.0/authorize`,
params: {
response_type: "code id_token",
response_mode: "query",
},
},
accessTokenUrl: tokenUrl,
requestTokenUrl: tokenUrl,
authorizationUrl: `${authorizeUrl}?response_type=code+id_token&response_mode=query`,
profileUrl: 'https://graph.microsoft.com/oidc/userinfo',
idToken: true,
profile: (profile) => {
let name = ''
token: {
url: `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${primaryUserFlow}/oauth2/v2.0/token`,
idToken: true,
},
jwks_uri: `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${primaryUserFlow}}/discovery/v2.0/keys`,
profile(profile) {
let name = ""

if (profile.name) {
// B2C "Display Name"
Expand All @@ -31,9 +32,10 @@ export default function AzureADB2C(options) {
}

return {
name,
id: profile.oid,
email: profile.emails[0]
name,
email: profile.emails[0],
image: null,
}
},
...options,
Expand Down
25 changes: 11 additions & 14 deletions src/providers/azure-ad.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
export default function AzureAD(options) {
const tenant = options.tenantId ?? 'common'
const tenant = options.tenantId ?? "common"

return {
id: 'azure-ad',
name: 'Azure Active Directory',
type: 'oauth',
version: '2.0',
params: {
grant_type: 'authorization_code'
},
accessTokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
authorizationUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query`,
profileUrl: 'https://graph.microsoft.com/v1.0/me/',
profile: (profile) => {
id: "azure-ad",
name: "Azure Active Directory",
type: "oauth",
authorization: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?response_mode=query`,
token: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
userinfo: "https://graph.microsoft.com/v1.0/me/",
profile(profile) {
return {
id: profile.id,
name: profile.displayName,
email: profile.userPrincipalName
email: profile.userPrincipalName,
image: null,
}
},
...options
...options,
}
}
Loading