mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
80 Commits
v4.0.0-bet
...
v4.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dcdb62dca | ||
|
|
1f4b7d8089 | ||
|
|
fedb84872d | ||
|
|
c0dddfb77f | ||
|
|
50fe115df6 | ||
|
|
cc17ddf8aa | ||
|
|
8644e553ed | ||
|
|
d1d0db43ea | ||
|
|
b01f6805d3 | ||
|
|
c44b860b9e | ||
|
|
22f74d7c4d | ||
|
|
2570168660 | ||
|
|
187a1474f5 | ||
|
|
4dc76749f2 | ||
|
|
35ee608d59 | ||
|
|
0f132de115 | ||
|
|
31426b9435 | ||
|
|
64b2a2c43b | ||
|
|
7beb3ff03b | ||
|
|
432876c011 | ||
|
|
15d1fab4c8 | ||
|
|
5e803cd34c | ||
|
|
78fa33312f | ||
|
|
932d05da70 | ||
|
|
e8a58a01b6 | ||
|
|
91de463a5e | ||
|
|
4a9d871698 | ||
|
|
c2119b15de | ||
|
|
0ce15c4a18 | ||
|
|
ead715219a | ||
|
|
8faa7553dd | ||
|
|
90a6a0084b | ||
|
|
cb844a2436 | ||
|
|
74558d6cc2 | ||
|
|
d03125a77b | ||
|
|
66d16f8bf4 | ||
|
|
be74dd0e7e | ||
|
|
9bf867ddcf | ||
|
|
0f460c22da | ||
|
|
887cb00877 | ||
|
|
75ca097ff7 | ||
|
|
bcb9383aec | ||
|
|
b953963101 | ||
|
|
4649f1968b | ||
|
|
45f4a69a4e | ||
|
|
2155c93a3c | ||
|
|
d5958571a4 | ||
|
|
ebecaa6a4b | ||
|
|
1c5173a818 | ||
|
|
35ce332cc6 | ||
|
|
ec295287f1 | ||
|
|
46978ac02f | ||
|
|
f546e550dd | ||
|
|
ac5b4db0f2 | ||
|
|
8bbffdd08c | ||
|
|
a22a0a36fd | ||
|
|
797272afe1 | ||
|
|
13e56bcf2f | ||
|
|
b0f7f87c04 | ||
|
|
9c0851c0f9 | ||
|
|
f5b3c29ab1 | ||
|
|
b4f2a0106a | ||
|
|
9c095b0532 | ||
|
|
0475964a0f | ||
|
|
ad6c13cdc9 | ||
|
|
591aa7cc7e | ||
|
|
9abb392b4e | ||
|
|
b89ae87fb1 | ||
|
|
3687d17724 | ||
|
|
b04ff82fb9 | ||
|
|
c11915ba9c | ||
|
|
24ee459f97 | ||
|
|
ac4851d238 | ||
|
|
84094b0ee7 | ||
|
|
f09ab4a04f | ||
|
|
067364381b | ||
|
|
6ee36b6842 | ||
|
|
5a89ab69d3 | ||
|
|
665445818e | ||
|
|
67cf2a11bb |
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -10,7 +10,13 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please provide the following information:
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
### Important :exclamation:
|
||||
Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead:
|
||||
- Docs related: https://github.com/nextauthjs/docs
|
||||
- Adapter related: https://github.com/nextauthjs/adapters
|
||||
|
||||
If you are in the correct repo, then proceed by providing the following information:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
11
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@@ -9,8 +9,14 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library. Please provide the following information:
|
||||
|
||||
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library.
|
||||
### Important :exclamation:
|
||||
Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead:
|
||||
- Docs related: https://github.com/nextauthjs/docs
|
||||
- Adapter related: https://github.com/nextauthjs/adapters
|
||||
|
||||
If you are in the correct repo, then proceed by providing the following information:
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
@@ -65,4 +71,3 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
It takes a lot of work 🏋🏻♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -43,6 +43,8 @@ app/package-lock.json
|
||||
app/yarn.lock
|
||||
app/prisma/migrations
|
||||
app/prisma/dev.db*
|
||||
app/dist
|
||||
app/next-auth
|
||||
|
||||
# VS
|
||||
/.vs/slnx.sqlite-journal
|
||||
@@ -50,6 +52,9 @@ app/prisma/dev.db*
|
||||
/.vs
|
||||
.vscode
|
||||
|
||||
# Jetbrains
|
||||
.idea
|
||||
|
||||
# GitHub Actions runner
|
||||
/actions-runner
|
||||
/_work
|
||||
|
||||
22
README.md
22
README.md
@@ -32,6 +32,11 @@ NextAuth.js is a complete open source authentication solution for [Next.js](http
|
||||
|
||||
It is designed from the ground up to support Next.js and Serverless.
|
||||
|
||||
This is the core repo for NextAuth.js. Check the repos below if you are interested in additional information:
|
||||
|
||||
- Docs related: https://github.com/nextauthjs/docs
|
||||
- Adapter related: https://github.com/nextauthjs/adapters
|
||||
|
||||
## Getting Started
|
||||
|
||||
```
|
||||
@@ -81,7 +86,8 @@ Advanced options allow you to define your own routines to handle controlling wha
|
||||
|
||||
### TypeScript
|
||||
|
||||
NextAuth.js comes with built-in types. For more information and usage, check out the [TypeScript section](https://next-auth.js.org/getting-started/typescript) in the documentation.
|
||||
NextAuth.js comes with built-in types. For more information and usage, check out
|
||||
the [TypeScript section](https://next-auth.js.org/getting-started/typescript) in the documentation.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -90,21 +96,24 @@ NextAuth.js comes with built-in types. For more information and usage, check out
|
||||
```javascript
|
||||
// pages/api/auth/[...nextauth].js
|
||||
import NextAuth from "next-auth"
|
||||
import Providers from "next-auth/providers"
|
||||
import AppleProvider from "next-auth/providers/apple"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
|
||||
export default NextAuth({
|
||||
secret: process.env.SECRET,
|
||||
providers: [
|
||||
// OAuth authentication providers
|
||||
Providers.Apple({
|
||||
AppleProvider({
|
||||
clientId: process.env.APPLE_ID,
|
||||
clientSecret: process.env.APPLE_SECRET,
|
||||
}),
|
||||
Providers.Google({
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
// Sign in with passwordless email link
|
||||
Providers.Email({
|
||||
EmailProvider({
|
||||
server: process.env.MAIL_SERVER,
|
||||
from: "<no-reply@example.com>",
|
||||
}),
|
||||
@@ -219,7 +228,8 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
|
||||
## Contributing
|
||||
|
||||
We're open to all community contributions! If you'd like to contribute in any way, please first read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/canary/CONTRIBUTING.md).
|
||||
We're open to all community contributions! If you'd like to contribute in any way, please first read
|
||||
our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/canary/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ const path = require("path")
|
||||
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.experiments = {
|
||||
topLevelAwait: true,
|
||||
}
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
|
||||
@@ -23,6 +23,8 @@ import CognitoProvider from "next-auth/providers/cognito"
|
||||
import SlackProvider from "next-auth/providers/slack"
|
||||
import Okta from "next-auth/providers/okta"
|
||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||
import OsuProvider from "next-auth/providers/osu"
|
||||
import AppleProvider from "next-auth/providers/apple"
|
||||
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
@@ -167,6 +169,14 @@ export const authOptions: NextAuthOptions = {
|
||||
tenantId: process.env.AZURE_B2C_TENANT_ID,
|
||||
primaryUserFlow: process.env.AZURE_B2C_PRIMARY_USER_FLOW,
|
||||
}),
|
||||
OsuProvider({
|
||||
clientId: process.env.OSU_CLIENT_ID,
|
||||
clientSecret: process.env.OSU_CLIENT_SECRET,
|
||||
}),
|
||||
AppleProvider({
|
||||
clientId: process.env.APPLE_ID,
|
||||
clientSecret: process.env.APPLE_SECRET,
|
||||
}),
|
||||
],
|
||||
secret: process.env.SECRET,
|
||||
debug: true,
|
||||
|
||||
@@ -4,7 +4,7 @@ body {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
li,
|
||||
|
||||
21
package-lock.json
generated
21
package-lock.json
generated
@@ -6935,20 +6935,6 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -20160,13 +20146,6 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
|
||||
26
package.json
26
package.json
@@ -5,6 +5,11 @@
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
"author": "Iain Collins <me@iaincollins.com>",
|
||||
"contributors": [
|
||||
"Balázs Orbán <info@balazsorban.com>",
|
||||
"Nico Domino <yo@ndo.dev>",
|
||||
"Lluis Agusti <hi@llu.lu>"
|
||||
],
|
||||
"main": "index.js",
|
||||
"module": "index.js",
|
||||
"types": "index.d.ts",
|
||||
@@ -139,10 +144,7 @@
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"extends": [
|
||||
"standard-with-typescript",
|
||||
"prettier"
|
||||
],
|
||||
"extends": ["standard-with-typescript", "prettier"],
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"next-env.d.ts",
|
||||
@@ -166,18 +168,12 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"./**/*test.js"
|
||||
],
|
||||
"files": ["./**/*test.js"],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"jest"
|
||||
]
|
||||
"extends": ["plugin:jest/recommended"],
|
||||
"plugins": ["jest"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -211,6 +207,10 @@
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/balazsorban44"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nextauth"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { ErrorType } from "./pages/error"
|
||||
export interface IncomingRequest {
|
||||
/** @default "http://localhost:3000" */
|
||||
host: string
|
||||
method: string
|
||||
method?: string
|
||||
cookies?: Record<string, string>
|
||||
headers?: Record<string, any>
|
||||
query?: Record<string, any>
|
||||
@@ -67,7 +67,7 @@ export async function NextAuthHandler<
|
||||
return render.error({ error: "configuration" })
|
||||
}
|
||||
|
||||
const { action, providerId, error } = req
|
||||
const { action, providerId, error, method = "GET" } = req
|
||||
|
||||
const { options, cookies } = await init({
|
||||
userOptions,
|
||||
@@ -77,7 +77,7 @@ export async function NextAuthHandler<
|
||||
callbackUrl: req.body?.callbackUrl ?? req.query?.callbackUrl,
|
||||
csrfToken: req.body?.csrfToken,
|
||||
cookies: req.cookies,
|
||||
isPost: req.method === "POST",
|
||||
isPost: method === "POST",
|
||||
})
|
||||
|
||||
const sessionStore = new SessionStore(
|
||||
@@ -86,7 +86,7 @@ export async function NextAuthHandler<
|
||||
options.logger
|
||||
)
|
||||
|
||||
if (req.method === "GET") {
|
||||
if (method === "GET") {
|
||||
const render = renderPage({ ...options, query: req.query, cookies })
|
||||
const { pages } = options
|
||||
switch (action) {
|
||||
@@ -119,9 +119,9 @@ export async function NextAuthHandler<
|
||||
const callback = await routes.callback({
|
||||
body: req.body,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
cookies: req.cookies,
|
||||
method,
|
||||
options,
|
||||
sessionStore,
|
||||
})
|
||||
@@ -165,7 +165,7 @@ export async function NextAuthHandler<
|
||||
return render.error({ error: error as ErrorType })
|
||||
default:
|
||||
}
|
||||
} else if (req.method === "POST") {
|
||||
} else if (method === "POST") {
|
||||
switch (action) {
|
||||
case "signin":
|
||||
// Verified CSRF Token required for all sign in routes
|
||||
@@ -201,9 +201,9 @@ export async function NextAuthHandler<
|
||||
const callback = await routes.callback({
|
||||
body: req.body,
|
||||
query: req.query,
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
cookies: req.cookies,
|
||||
method,
|
||||
options,
|
||||
sessionStore,
|
||||
})
|
||||
@@ -228,6 +228,6 @@ export async function NextAuthHandler<
|
||||
|
||||
return {
|
||||
status: 400,
|
||||
body: `Error: Action ${action} with HTTP ${req.method} is not supported by NextAuth.js` as any,
|
||||
body: `Error: Action ${action} with HTTP ${method} is not supported by NextAuth.js` as any,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ export function assertConfig(
|
||||
): ConfigError | WarningCode | undefined {
|
||||
const { options, req } = params
|
||||
|
||||
if (!req.query?.nextauth) {
|
||||
// req.query isn't defined when asserting `getServerSession` for example
|
||||
if (!req.query?.nextauth && !req.action) {
|
||||
return new MissingAPIRoute(
|
||||
"Cannot find [...nextauth].{js,ts} in `/pages/api/auth`. Make sure the filename is written correctly."
|
||||
)
|
||||
@@ -55,7 +56,7 @@ export function assertConfig(
|
||||
const onlyCredentials = !options.providers.some(
|
||||
(p) => p.type !== "credentials"
|
||||
)
|
||||
if (dbStrategy || onlyCredentials) {
|
||||
if (dbStrategy && onlyCredentials) {
|
||||
return new UnsupportedStrategy(
|
||||
"Signin in with credentials only supported if JWT strategy is enabled"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function oAuthCallback(params: {
|
||||
options: InternalOptions<"oauth">
|
||||
query: IncomingRequest["query"]
|
||||
body: IncomingRequest["body"]
|
||||
method: IncomingRequest["method"]
|
||||
method: Required<IncomingRequest>["method"]
|
||||
cookies: IncomingRequest["cookies"]
|
||||
}): Promise<GetProfileResult & { cookies?: OutgoingResponse["cookies"] }> {
|
||||
const { options, query, body, method, cookies } = params
|
||||
@@ -137,10 +137,6 @@ export default async function oAuthCallback(params: {
|
||||
})
|
||||
}
|
||||
|
||||
// If a user object is supplied (e.g. Apple provider) add it to the profile object
|
||||
// TODO: Remove/extract to Apple provider?
|
||||
profile.user = JSON.parse(body?.user ?? query?.user ?? null)
|
||||
|
||||
const profileResult = await getProfile({
|
||||
profile,
|
||||
provider,
|
||||
|
||||
@@ -44,12 +44,7 @@ export async function openidClient(
|
||||
// and https://github.com/nextauthjs/next-auth/issues/3067
|
||||
client[custom.clock_tolerance] = 10
|
||||
|
||||
// https://github.com/nextauthjs/next-auth/discussions/3186
|
||||
if (typeof provider.httpOptions?.timeout === "number") {
|
||||
custom.setHttpOptionsDefaults({
|
||||
timeout: provider.httpOptions.timeout,
|
||||
})
|
||||
}
|
||||
if (provider.httpOptions) custom.setHttpOptionsDefaults(provider.httpOptions)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -87,15 +87,15 @@ export default function ErrorPage(props: ErrorProps) {
|
||||
status,
|
||||
html: (
|
||||
<div className="error">
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
{ theme?.brandColor && <style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
:root {
|
||||
--brand-color: ${theme?.brandColor};
|
||||
--brand-color: ${theme?.brandColor}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/> }
|
||||
{theme?.logo && <img src={theme.logo} alt="Logo" className="logo" />}
|
||||
<div className="card">
|
||||
<h1>{heading}</h1>
|
||||
|
||||
@@ -51,6 +51,7 @@ export default function SigninPage(props: SignInServerPageParams) {
|
||||
EmailSignin: "Check your email inbox.",
|
||||
CredentialsSignin:
|
||||
"Sign in failed. Check the details you provided are correct.",
|
||||
SessionRequired: "Please sign in to access this page.",
|
||||
default: "Unable to sign in.",
|
||||
}
|
||||
|
||||
@@ -58,7 +59,8 @@ export default function SigninPage(props: SignInServerPageParams) {
|
||||
|
||||
return (
|
||||
<div className="signin">
|
||||
<style
|
||||
|
||||
{ theme.brandColor && <style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
:root {
|
||||
@@ -66,7 +68,7 @@ export default function SigninPage(props: SignInServerPageParams) {
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
/> }
|
||||
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
|
||||
<div className="card">
|
||||
{error && (
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function SignoutPage(props: SignoutProps) {
|
||||
|
||||
return (
|
||||
<div className="signout">
|
||||
<style
|
||||
{ theme.brandColor && <style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
:root {
|
||||
@@ -20,7 +20,7 @@ export default function SignoutPage(props: SignoutProps) {
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
/> }
|
||||
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
|
||||
<div className="card">
|
||||
<h1>Signout</h1>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function VerifyRequestPage(props: VerifyRequestPageProps) {
|
||||
|
||||
return (
|
||||
<div className="verify-request">
|
||||
<style
|
||||
{ theme.brandColor && <style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
:root {
|
||||
@@ -19,7 +19,7 @@ export default function VerifyRequestPage(props: VerifyRequestPageProps) {
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
/> }
|
||||
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
|
||||
<div className="card">
|
||||
<h1>Check your email</h1>
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { User } from "../.."
|
||||
export default async function callback(params: {
|
||||
options: InternalOptions<"oauth" | "credentials" | "email">
|
||||
query: IncomingRequest["query"]
|
||||
method: IncomingRequest["method"]
|
||||
method: Required<IncomingRequest>["method"]
|
||||
body: IncomingRequest["body"]
|
||||
headers: IncomingRequest["headers"]
|
||||
cookies: IncomingRequest["cookies"]
|
||||
|
||||
@@ -69,7 +69,7 @@ label {
|
||||
text-align: left;
|
||||
margin-bottom: 0.25rem;
|
||||
display: block;
|
||||
color: #666;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
input[type] {
|
||||
@@ -258,5 +258,5 @@ a.site {
|
||||
}
|
||||
|
||||
.section-header {
|
||||
color: var(--brand-color);
|
||||
color: var(--brand-color, var(--color-text));
|
||||
}
|
||||
|
||||
@@ -18,17 +18,18 @@ async function NextAuthNextHandler(
|
||||
res: NextApiResponse,
|
||||
options: NextAuthOptions
|
||||
) {
|
||||
const { nextauth, ...query } = req.query
|
||||
const handler = await NextAuthHandler({
|
||||
req: {
|
||||
host: (process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL) as string,
|
||||
body: req.body,
|
||||
query: req.query,
|
||||
query,
|
||||
cookies: req.cookies,
|
||||
headers: req.headers,
|
||||
method: req.method ?? "GET",
|
||||
action: req.query.nextauth[0] as NextAuthAction,
|
||||
providerId: req.query.nextauth[1],
|
||||
error: (req.query.error as string | undefined) ?? req.query.nextauth[1],
|
||||
method: req.method,
|
||||
action: nextauth?.[0] as NextAuthAction,
|
||||
providerId: nextauth?.[1],
|
||||
error: (req.query.error as string | undefined) ?? nextauth?.[1],
|
||||
},
|
||||
options,
|
||||
})
|
||||
|
||||
179
src/providers/42-school.ts
Normal file
179
src/providers/42-school.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface UserData {
|
||||
id: number
|
||||
email: string
|
||||
login: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
usual_full_name: null | string
|
||||
usual_first_name: null | string
|
||||
url: string
|
||||
phone: "hidden" | string | null
|
||||
displayname: string
|
||||
image_url: string | null
|
||||
"staff?": boolean
|
||||
correction_point: number
|
||||
pool_month: string | null
|
||||
pool_year: string | null
|
||||
location: string | null
|
||||
wallet: number
|
||||
anonymize_date: string
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
alumni: boolean
|
||||
"is_launched?": boolean
|
||||
}
|
||||
|
||||
export interface CursusUser {
|
||||
grade: string | null
|
||||
level: number
|
||||
skills: Array<{ id: number; name: string; level: number }>
|
||||
blackholed_at: string | null
|
||||
id: number
|
||||
begin_at: string | null
|
||||
end_at: string | null
|
||||
cursus_id: number
|
||||
has_coalition: boolean
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
user: UserData
|
||||
cursus: { id: number; created_at: string; name: string; slug: string }
|
||||
}
|
||||
|
||||
export interface ProjectUser {
|
||||
id: number
|
||||
occurrence: number
|
||||
final_mark: number | null
|
||||
status: "in_progress" | "finished"
|
||||
"validated?": boolean | null
|
||||
current_team_id: number
|
||||
project: {
|
||||
id: number
|
||||
name: string
|
||||
slug: string
|
||||
parent_id: number | null
|
||||
}
|
||||
cursus_ids: number[]
|
||||
marked_at: string | null
|
||||
marked: boolean
|
||||
retriable_at: string | null
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface Achievement {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
tier: "none" | "easy" | "medium" | "hard" | "challenge"
|
||||
kind: "scolarity" | "project" | "pedagogy" | "scolarity"
|
||||
visible: boolean
|
||||
image: string | null
|
||||
nbr_of_success: number | null
|
||||
users_url: string
|
||||
}
|
||||
|
||||
export interface LanguagesUser {
|
||||
id: number
|
||||
language_id: number
|
||||
user_id: number
|
||||
position: number
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface TitlesUser {
|
||||
id: number
|
||||
user_id: number
|
||||
title_id: number
|
||||
selected: boolean
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface ExpertisesUser {
|
||||
id: number
|
||||
expertise_id: number
|
||||
interested: boolean
|
||||
value: number
|
||||
contact_me: boolean
|
||||
created_at: string
|
||||
user_id: number
|
||||
}
|
||||
|
||||
export interface Campus {
|
||||
id: number
|
||||
name: string
|
||||
time_zone: string
|
||||
language: {
|
||||
id: number
|
||||
name: string
|
||||
identifier: string
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
}
|
||||
users_count: number
|
||||
vogsphere_id: number
|
||||
country: string
|
||||
address: string
|
||||
zip: string
|
||||
city: string
|
||||
website: string
|
||||
facebook: string
|
||||
twitter: string
|
||||
active: boolean
|
||||
email_extension: string
|
||||
default_hidden_phone: boolean
|
||||
}
|
||||
|
||||
export interface CampusUser {
|
||||
id: number
|
||||
user_id: number
|
||||
campus_id: number
|
||||
is_primary: boolean
|
||||
created_at: string
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface FortyTwoProfile extends UserData {
|
||||
groups: Array<{ id: string; name: string }>
|
||||
cursus_users: CursusUser[]
|
||||
projects_users: ProjectUser[]
|
||||
languages_users: LanguagesUser[]
|
||||
achievements: Achievement[]
|
||||
titles: Array<{ id: string; name: string }>
|
||||
titles_users: TitlesUser[]
|
||||
partnerships: any[]
|
||||
patroned: any[]
|
||||
patroning: any[]
|
||||
expertises_users: ExpertisesUser[]
|
||||
roles: Array<{ id: string; name: string }>
|
||||
campus: Campus[]
|
||||
campus_users: CampusUser[]
|
||||
user: any | null
|
||||
}
|
||||
|
||||
export default function FortyTwo<
|
||||
P extends Record<string, any> = FortyTwoProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
return {
|
||||
id: "42-school",
|
||||
name: "42 School",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://api.intra.42.fr/oauth/authorize",
|
||||
params: { scope: "public" },
|
||||
},
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function FortyTwo(options) {
|
||||
return {
|
||||
id: "42-school",
|
||||
name: "42 School",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://api.intra.42.fr/oauth/authorize",
|
||||
params: { scope: "public" },
|
||||
},
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Apple(options) {
|
||||
return {
|
||||
id: "apple",
|
||||
name: "Apple",
|
||||
type: "oauth",
|
||||
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 be returned on first login
|
||||
const name = profile.user
|
||||
? profile.user.name.firstName + " " + profile.user.name.lastName
|
||||
: null
|
||||
|
||||
return {
|
||||
id: profile.sub,
|
||||
name,
|
||||
email: profile.email,
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
checks: ["none"], // REVIEW: Apple does not support state, as far as I know. Can we use "pkce" then?
|
||||
options,
|
||||
}
|
||||
}
|
||||
122
src/providers/apple.ts
Normal file
122
src/providers/apple.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
/**
|
||||
* See more at:
|
||||
* [Retrieve the User's Information from Apple ID Servers
|
||||
](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773)
|
||||
*/
|
||||
export interface AppleProfile {
|
||||
/**
|
||||
* The issuer registered claim identifies the principal that issued the identity token.
|
||||
* Since Apple generates the token, the value is `https://appleid.apple.com`.
|
||||
*/
|
||||
iss: "https://appleid.apple.com"
|
||||
/**
|
||||
* The audience registered claim identifies the recipient for which the identity token is intended.
|
||||
* Since the token is meant for your application, the value is the `client_id` from your developer account.
|
||||
*/
|
||||
aud: string
|
||||
/**
|
||||
* The issued at registered claim indicates the time at which Apple issued the identity token,
|
||||
* in terms of the number of seconds since Epoch, in UTC.
|
||||
*/
|
||||
iat: number
|
||||
|
||||
/**
|
||||
* The expiration time registered identifies the time on or after which the identity token expires,
|
||||
* in terms of number of seconds since Epoch, in UTC.
|
||||
* The value must be greater than the current date/time when verifying the token.
|
||||
*/
|
||||
exp: number
|
||||
/**
|
||||
* The subject registered claim identifies the principal that's the subject of the identity token.
|
||||
* Since this token is meant for your application, the value is the unique identifier for the user.
|
||||
*/
|
||||
sub: string
|
||||
/**
|
||||
* A String value used to associate a client session and the identity token.
|
||||
* This value mitigates replay attacks and is present only if passed during the authorization request.
|
||||
*/
|
||||
nonce: string
|
||||
|
||||
/**
|
||||
* A Boolean value that indicates whether the transaction is on a nonce-supported platform.
|
||||
* If you sent a nonce in the authorization request but don't see the nonce claim in the identity token,
|
||||
* check this claim to determine how to proceed.
|
||||
* If this claim returns true, you should treat nonce as mandatory and fail the transaction;
|
||||
* otherwise, you can proceed treating the nonce as options.
|
||||
*/
|
||||
nonce_supported: boolean
|
||||
|
||||
/**
|
||||
* A String value representing the user's email address.
|
||||
* The email address is either the user's real email address or the proxy address,
|
||||
* depending on their status private email relay service.
|
||||
*/
|
||||
email: string
|
||||
|
||||
/**
|
||||
* A String or Boolean value that indicates whether the service has verified the email.
|
||||
* The value of this claim is always true, because the servers only return verified email addresses.
|
||||
* The value can either be a String (`"true"`) or a Boolean (`true`).
|
||||
*/
|
||||
email_verified: "true" | true
|
||||
|
||||
/**
|
||||
* A String or Boolean value that indicates whether the email shared by the user is the proxy address.
|
||||
* The value can either be a String (`"true"` or `"false"`) or a Boolean (`true` or `false`).
|
||||
*/
|
||||
is_private_email: boolean | "true" | "false"
|
||||
|
||||
/**
|
||||
* An Integer value that indicates whether the user appears to be a real person.
|
||||
* Use the value of this claim to mitigate fraud. The possible values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal).
|
||||
* For more information, see [`ASUserDetectionStatus`](https://developer.apple.com/documentation/authenticationservices/asuserdetectionstatus).
|
||||
* This claim is present only on iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14 and later;
|
||||
* the claim isn't present or supported for web-based apps.
|
||||
*/
|
||||
real_user_status: 0 | 1 | 2
|
||||
|
||||
/**
|
||||
* A String value representing the transfer identifier used to migrate users to your team.
|
||||
* This claim is present only during the 60-day transfer period after an you transfer an app.
|
||||
* For more information, see [Bringing New Apps and Users into Your Team](https://developer.apple.com/documentation/sign_in_with_apple/bringing_new_apps_and_users_into_your_team).
|
||||
*/
|
||||
transfer_sub: string
|
||||
at_hash: string
|
||||
auth_time: number
|
||||
}
|
||||
|
||||
export default function Apple<P extends Record<string, any> = AppleProfile>(
|
||||
options: Omit<OAuthUserConfig<P>, "clientSecret"> & {
|
||||
/**
|
||||
* Apple requires the client secret to be a JWT. You can generate one using the following script:
|
||||
* https://bal.so/apple-gen-secret
|
||||
*
|
||||
* Read more: [Creating the Client Secret
|
||||
](https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048)
|
||||
*/
|
||||
clientSecret: string
|
||||
}
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "apple",
|
||||
name: "Apple",
|
||||
type: "oauth",
|
||||
wellKnown: "https://appleid.apple.com/.well-known/openid-configuration",
|
||||
authorization: {
|
||||
params: { scope: "name email", response_mode: "form_post" },
|
||||
},
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
checks: ["pkce"],
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Atlassian(options) {
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
interface AtlassianProfile {
|
||||
account_id: string
|
||||
name: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
export default function Atlassian<P extends AtlassianProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "atlassian",
|
||||
name: "Atlassian",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://auth.atlassian.com/oauth/authorize",
|
||||
url: "https://auth.atlassian.com/authorize",
|
||||
params: {
|
||||
audience: "api.atlassian.com",
|
||||
prompt: "consent",
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "./oauth"
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface Auth0Profile {
|
||||
sub: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface AzureB2CProfile {
|
||||
exp: number
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "./oauth"
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface AzureADProfile {
|
||||
sub: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface CognitoProfile {
|
||||
sub: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IncomingRequest } from "src/core"
|
||||
import { CommonProviderOptions } from "."
|
||||
import { User, Awaitable } from ".."
|
||||
import type { IncomingRequest } from "../core"
|
||||
import type { CommonProviderOptions } from "."
|
||||
import type { User, Awaitable } from ".."
|
||||
|
||||
export interface CredentialInput {
|
||||
label?: string
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createTransport } from "nodemailer"
|
||||
|
||||
import { CommonProviderOptions } from "."
|
||||
import { Options as SMTPConnectionOptions } from "nodemailer/lib/smtp-connection"
|
||||
import { Awaitable } from ".."
|
||||
import type { CommonProviderOptions } from "."
|
||||
import type { Options as SMTPConnectionOptions } from "nodemailer/lib/smtp-connection"
|
||||
import type { Awaitable } from ".."
|
||||
|
||||
export interface EmailConfig extends CommonProviderOptions {
|
||||
type: "email"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function EVEOnline(options) {
|
||||
return {
|
||||
id: "eveonline",
|
||||
name: "EVE Online",
|
||||
type: "oauth",
|
||||
authorization: "https://login.eveonline.com/oauth/authorize",
|
||||
token: "https://login.eveonline.com/oauth/token",
|
||||
userinfo: "https://login.eveonline.com/oauth/verify",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.CharacterID,
|
||||
name: profile.CharacterName,
|
||||
email: null,
|
||||
image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
38
src/providers/eveonline.ts
Normal file
38
src/providers/eveonline.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface EVEOnlineProfile {
|
||||
CharacterID: number
|
||||
CharacterName: string
|
||||
ExpiresOn: string
|
||||
Scopes: string
|
||||
TokenType: string
|
||||
CharacterOwnerHash: string
|
||||
IntellectualProperty: string
|
||||
}
|
||||
|
||||
export default function EVEOnline<
|
||||
P extends Record<string, any> = EVEOnlineProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
return {
|
||||
id: "eveonline",
|
||||
name: "EVE Online",
|
||||
type: "oauth",
|
||||
wellKnown:
|
||||
"https://login.eveonline.com/.well-known/oauth-authorization-server",
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "publicData",
|
||||
},
|
||||
},
|
||||
idToken: true,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.CharacterID,
|
||||
name: profile.CharacterName,
|
||||
email: null,
|
||||
image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Profile } from ".."
|
||||
import { OAuthConfig, OAuthUserConfig } from "./oauth"
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface FacebookProfile extends Profile {
|
||||
export interface FacebookProfile {
|
||||
id: string
|
||||
picture: { data: { url: string } }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,31 @@ export default function GitHub(options) {
|
||||
type: "oauth",
|
||||
authorization: "https://github.com/login/oauth/authorize?scope=read:user+user:email",
|
||||
token: "https://github.com/login/oauth/access_token",
|
||||
userinfo: "https://api.github.com/user",
|
||||
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(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "./oauth"
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface GoogleProfile {
|
||||
sub: string
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { OAuthConfig, OAuthProvider, OAuthProviderType } from "./oauth"
|
||||
import type { OAuthConfig, OAuthProvider, OAuthProviderType } from "./oauth"
|
||||
|
||||
import { EmailConfig, EmailProvider, EmailProviderType } from "./email"
|
||||
import type { EmailConfig, EmailProvider, EmailProviderType } from "./email"
|
||||
|
||||
import {
|
||||
import type {
|
||||
CredentialsConfig,
|
||||
CredentialsProvider,
|
||||
CredentialsProviderType,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface KeycloakProfile {
|
||||
exp: number
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface LineProfile {
|
||||
iss: string;
|
||||
sub: string;
|
||||
aud: string;
|
||||
exp: number;
|
||||
iat: number;
|
||||
amr: string[];
|
||||
name: string;
|
||||
picture: string;
|
||||
user: any;
|
||||
iss: string
|
||||
sub: string
|
||||
aud: string
|
||||
exp: number
|
||||
iat: number
|
||||
amr: string[]
|
||||
name: string
|
||||
picture: string
|
||||
user: any
|
||||
}
|
||||
|
||||
export default function LINE<
|
||||
P extends Record<string, any> = LineProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
export default function LINE<P extends Record<string, any> = LineProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "line",
|
||||
name: "LINE",
|
||||
@@ -33,6 +33,6 @@ export default function LINE<
|
||||
client: {
|
||||
id_token_signed_response_alg: "HS256",
|
||||
},
|
||||
options
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
interface Identifier {
|
||||
identifier: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonProviderOptions } from "../providers"
|
||||
import { Profile, TokenSet, User, Awaitable } from ".."
|
||||
import type { CommonProviderOptions } from "../providers"
|
||||
import type { Profile, TokenSet, User, Awaitable } from ".."
|
||||
|
||||
import type {
|
||||
AuthorizationParameters,
|
||||
@@ -115,10 +115,7 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
|
||||
client?: Partial<ClientMetadata>
|
||||
jwks?: { keys: JWK[] }
|
||||
clientId?: string
|
||||
clientSecret?:
|
||||
| string
|
||||
// TODO: only allow for Apple
|
||||
| Record<"appleId" | "teamId" | "privateKey" | "keyId", string>
|
||||
clientSecret?: string
|
||||
/**
|
||||
* If set to `true`, the user information will be extracted
|
||||
* from the `id_token` claims, instead of
|
||||
@@ -134,7 +131,7 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
|
||||
// TODO: only allow for some
|
||||
issuer?: string
|
||||
/** Read more at: https://github.com/panva/node-openid-client/tree/main/docs#customizing-http-requests */
|
||||
httpOptions?: Pick<HttpOptions, "timeout">
|
||||
httpOptions?: HttpOptions
|
||||
|
||||
/**
|
||||
* The options provided by the user.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface OktaProfile {
|
||||
iss: string
|
||||
|
||||
77
src/providers/osu.ts
Normal file
77
src/providers/osu.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface OsuUserCompact {
|
||||
avatar_url: string
|
||||
country_code: string
|
||||
default_group: string
|
||||
id: string
|
||||
is_active: boolean
|
||||
is_bot: boolean
|
||||
is_deleted: boolean
|
||||
is_online: boolean
|
||||
is_supporter: boolean
|
||||
last_visit: Date | null
|
||||
pm_friends_only: boolean
|
||||
profile_colour: string | null
|
||||
username: string
|
||||
}
|
||||
|
||||
export interface OsuProfile extends OsuUserCompact {
|
||||
discord: string | null
|
||||
has_supported: boolean
|
||||
interests: string | null
|
||||
join_date: Date
|
||||
kudosu: {
|
||||
available: number
|
||||
total: number
|
||||
}
|
||||
location: string | null
|
||||
max_blocks: number
|
||||
max_friends: number
|
||||
occupation: string | null
|
||||
playmode: string
|
||||
playstyle: string[]
|
||||
post_count: number
|
||||
profile_order: string[]
|
||||
title: string | null
|
||||
title_url: string | null
|
||||
twitter: string | null
|
||||
website: string | null
|
||||
country: {
|
||||
code: string
|
||||
name: string
|
||||
}
|
||||
cover: {
|
||||
custom_url: string | null
|
||||
url: string
|
||||
id: number | null
|
||||
}
|
||||
is_restricted: boolean
|
||||
}
|
||||
|
||||
export default function Osu<P extends Record<string, any> = OsuProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "osu",
|
||||
name: "Osu!",
|
||||
type: "oauth",
|
||||
token: "https://osu.ppy.sh/oauth/token",
|
||||
authorization: {
|
||||
url: "https://osu.ppy.sh/oauth/authorize",
|
||||
params: {
|
||||
scope: "identify",
|
||||
},
|
||||
},
|
||||
userinfo: "https://osu.ppy.sh/api/v2/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
email: null,
|
||||
name: profile.username,
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
59
src/providers/pipedrive.ts
Normal file
59
src/providers/pipedrive.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface PipedriveProfile {
|
||||
success: boolean
|
||||
data: {
|
||||
id: number
|
||||
name: string
|
||||
default_currency?: string
|
||||
locale?: string
|
||||
lang?: number
|
||||
email: string
|
||||
phone?: string
|
||||
activated?: boolean
|
||||
last_login?: Date
|
||||
created?: Date
|
||||
modified?: Date
|
||||
signup_flow_variation?: string
|
||||
has_created_company?: boolean
|
||||
is_admin?: number
|
||||
active_flag?: boolean
|
||||
timezone_name?: string
|
||||
timezone_offset?: string
|
||||
role_id?: number
|
||||
icon_url?: string
|
||||
is_you?: boolean
|
||||
company_id?: number
|
||||
company_name?: string
|
||||
company_domain?: string
|
||||
company_country?: string
|
||||
company_industry?: string
|
||||
language?: {
|
||||
language_code?: string
|
||||
country_code?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Pipedrive<
|
||||
P extends Record<string, any> = PipedriveProfile
|
||||
>(options: OAuthUserConfig<P>): OAuthConfig<P> {
|
||||
return {
|
||||
id: "pipedrive",
|
||||
name: "Pipedrive",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
authorization: "https://oauth.pipedrive.com/oauth/authorize",
|
||||
token: "https://oauth.pipedrive.com/oauth/token",
|
||||
userinfo: "https://api.pipedrive.com/users/me",
|
||||
profile: ({ data: profile }) => {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.icon_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface SlackProfile {
|
||||
ok: boolean
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface SpotifyImage {
|
||||
url: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface TwitchProfile {
|
||||
sub: string
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Twitter(options) {
|
||||
return {
|
||||
id: "twitter",
|
||||
name: "Twitter",
|
||||
type: "oauth",
|
||||
version: "1.0A",
|
||||
authorization: "https://api.twitter.com/oauth/authenticate",
|
||||
accessTokenUrl: "https://api.twitter.com/oauth/access_token",
|
||||
requestTokenUrl: "https://api.twitter.com/oauth/request_token",
|
||||
profileUrl:
|
||||
"https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id_str,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.profile_image_url_https.replace(
|
||||
/_normal\.(jpg|png|gif)$/,
|
||||
".$1"
|
||||
),
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
124
src/providers/twitter.ts
Normal file
124
src/providers/twitter.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface TwitterProfile {
|
||||
id: number
|
||||
id_str: string
|
||||
name: string
|
||||
screen_name: string
|
||||
location: string
|
||||
description: string
|
||||
url: string
|
||||
entities: {
|
||||
url: {
|
||||
urls: Array<{
|
||||
url: string
|
||||
expanded_url: string
|
||||
display_url: string
|
||||
indices: number[]
|
||||
}>
|
||||
}
|
||||
description: {
|
||||
urls: any[]
|
||||
}
|
||||
}
|
||||
protected: boolean
|
||||
followers_count: number
|
||||
friends_count: number
|
||||
listed_count: number
|
||||
created_at: string
|
||||
favourites_count: number
|
||||
utc_offset?: any
|
||||
time_zone?: any
|
||||
geo_enabled: boolean
|
||||
verified: boolean
|
||||
statuses_count: number
|
||||
lang?: any
|
||||
status: {
|
||||
created_at: string
|
||||
id: number
|
||||
id_str: string
|
||||
text: string
|
||||
truncated: boolean
|
||||
entities: {
|
||||
hashtags: any[]
|
||||
symbols: any[]
|
||||
user_mentions: Array<{
|
||||
screen_name: string
|
||||
name: string
|
||||
id: number
|
||||
id_str: string
|
||||
indices: number[]
|
||||
}>
|
||||
urls: any[]
|
||||
}
|
||||
source: string
|
||||
in_reply_to_status_id: number
|
||||
in_reply_to_status_id_str: string
|
||||
in_reply_to_user_id: number
|
||||
in_reply_to_user_id_str: string
|
||||
in_reply_to_screen_name: string
|
||||
geo?: any
|
||||
coordinates?: any
|
||||
place?: any
|
||||
contributors?: any
|
||||
is_quote_status: boolean
|
||||
retweet_count: number
|
||||
favorite_count: number
|
||||
favorited: boolean
|
||||
retweeted: boolean
|
||||
lang: string
|
||||
}
|
||||
contributors_enabled: boolean
|
||||
is_translator: boolean
|
||||
is_translation_enabled: boolean
|
||||
profile_background_color: string
|
||||
profile_background_image_url: string
|
||||
profile_background_image_url_https: string
|
||||
profile_background_tile: boolean
|
||||
profile_image_url: string
|
||||
profile_image_url_https: string
|
||||
profile_banner_url: string
|
||||
profile_link_color: string
|
||||
profile_sidebar_border_color: string
|
||||
profile_sidebar_fill_color: string
|
||||
profile_text_color: string
|
||||
profile_use_background_image: boolean
|
||||
has_extended_profile: boolean
|
||||
default_profile: boolean
|
||||
default_profile_image: boolean
|
||||
following: boolean
|
||||
follow_request_sent: boolean
|
||||
notifications: boolean
|
||||
translator_type: string
|
||||
withheld_in_countries: any[]
|
||||
suspended: boolean
|
||||
needs_phone_verification: boolean
|
||||
}
|
||||
|
||||
export default function Twitter<P extends Record<string, any> = TwitterProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "twitter",
|
||||
name: "Twitter",
|
||||
type: "oauth",
|
||||
version: "1.0A",
|
||||
authorization: "https://api.twitter.com/oauth/authenticate",
|
||||
accessTokenUrl: "https://api.twitter.com/oauth/access_token",
|
||||
requestTokenUrl: "https://api.twitter.com/oauth/request_token",
|
||||
profileUrl:
|
||||
"https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id_str,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.profile_image_url_https.replace(
|
||||
/_normal\.(jpg|png|gif)$/,
|
||||
".$1"
|
||||
),
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,9 @@ export default function Yandex(options) {
|
||||
id: profile.id,
|
||||
name: profile.real_name,
|
||||
email: profile.default_email,
|
||||
image: profile.is_avatar_empty ? null : `https://avatars.yandex.net/get-yapic/${profile.default_avatar_id}/islands-200`,
|
||||
image: profile.is_avatar_empty
|
||||
? null
|
||||
: `https://avatars.yandex.net/get-yapic/${profile.default_avatar_id}/islands-200`,
|
||||
}
|
||||
},
|
||||
options,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** @type {import(".").OAuthProvider} */
|
||||
export default function Zoom(options) {
|
||||
return {
|
||||
id: "zoom",
|
||||
name: "Zoom",
|
||||
type: "oauth",
|
||||
authorization: "https://zoom.us/oauth/authorize",
|
||||
token: "https://zoom.us/oauth/token",
|
||||
userinfo: "https://api.zoom.us/v2/users/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: `${profile.first_name} ${profile.last_name}`,
|
||||
email: profile.email,
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
52
src/providers/zoom.ts
Normal file
52
src/providers/zoom.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface ZoomProfile {
|
||||
id: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
email: string
|
||||
type: number
|
||||
role_name: string
|
||||
pmi: number
|
||||
use_pmi: boolean
|
||||
vanity_url: string
|
||||
personal_meeting_url: string
|
||||
timezone: string
|
||||
verified: number
|
||||
dept: string
|
||||
created_at: string
|
||||
last_login_time: string
|
||||
last_client_version: string
|
||||
pic_url: string
|
||||
host_key: string
|
||||
jid: string
|
||||
group_ids: string[]
|
||||
im_group_ids: string[]
|
||||
account_id: string
|
||||
language: string
|
||||
phone_country: string
|
||||
phone_number: string
|
||||
status: string
|
||||
}
|
||||
|
||||
export default function Zoom<P extends Record<string, any> = ZoomProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "zoom",
|
||||
name: "Zoom",
|
||||
type: "oauth",
|
||||
authorization: "https://zoom.us/oauth/authorize?scope",
|
||||
token: "https://zoom.us/oauth/token",
|
||||
userinfo: "https://api.zoom.us/v2/users/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: `${profile.first_name} ${profile.last_name}`,
|
||||
email: profile.email,
|
||||
image: profile.pic_url,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user