Compare commits
19 Commits
@auth/core
...
@auth/edge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea65134a84 | ||
|
|
016b22833c | ||
|
|
67dbbb2cdf | ||
|
|
f87e66f0b3 | ||
|
|
b1c46809f5 | ||
|
|
b9e9722b74 | ||
|
|
57f75c7839 | ||
|
|
e20eb5b583 | ||
|
|
fb7c5f9ef6 | ||
|
|
e986369906 | ||
|
|
f3c64a85c9 | ||
|
|
4f3241f8dd | ||
|
|
65043ba471 | ||
|
|
46c5a97a0e | ||
|
|
9f99066b19 | ||
|
|
e266001c28 | ||
|
|
f1eb45f3c1 | ||
|
|
f621627914 | ||
|
|
2b6ad02bba |
4
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -36,10 +36,12 @@ body:
|
||||
- "Beyond Identity"
|
||||
- "Box"
|
||||
- "Bungie"
|
||||
- "ClickUp"
|
||||
- "Cognito"
|
||||
- "Coinbase"
|
||||
- "Descope"
|
||||
- "Discord"
|
||||
- "Dribbble"
|
||||
- "Dropbox"
|
||||
- "EVE Online"
|
||||
- "Facebook"
|
||||
@@ -58,6 +60,7 @@ body:
|
||||
- "LinkedIn"
|
||||
- "Mailchimp"
|
||||
- "Mail.ru"
|
||||
- "Mastodon"
|
||||
- "Medium"
|
||||
- "Naver"
|
||||
- "Netlify"
|
||||
@@ -73,6 +76,7 @@ body:
|
||||
- "Slack"
|
||||
- "Spotify"
|
||||
- "Strava"
|
||||
- "Tiktok"
|
||||
- "Todoist"
|
||||
- "Trakt"
|
||||
- "Twitch"
|
||||
|
||||
@@ -59,6 +59,10 @@ WIKIMEDIA_SECRET=
|
||||
YANDEX_ID=
|
||||
YANDEX_SECRET=
|
||||
|
||||
# ClickUp OAuth. https://clickup.com/api/
|
||||
CLICK_UP_ID=
|
||||
CLICK_UP_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
EMAIL_FROM=user@gmail.com
|
||||
|
||||
1
apps/dev/nextjs/.gitignore
vendored
@@ -2,3 +2,4 @@ node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
dbschema/edgeql-js
|
||||
|
||||
71
apps/dev/nextjs/dbschema/default.esdl
Normal file
@@ -0,0 +1,71 @@
|
||||
module default {
|
||||
type User {
|
||||
property name -> str;
|
||||
required property email -> str {
|
||||
constraint exclusive;
|
||||
}
|
||||
property emailVerified -> datetime;
|
||||
property image -> str;
|
||||
multi link accounts := .<user[is Account];
|
||||
multi link sessions := .<user[is Session];
|
||||
property createdAt -> datetime {
|
||||
default := datetime_current();
|
||||
};
|
||||
}
|
||||
|
||||
type Account {
|
||||
required property userId := .user.id;
|
||||
required property type -> str;
|
||||
required property provider -> str;
|
||||
required property providerAccountId -> str {
|
||||
constraint exclusive;
|
||||
};
|
||||
property refresh_token -> str;
|
||||
property access_token -> str;
|
||||
property expires_at -> int64;
|
||||
property token_type -> str;
|
||||
property scope -> str;
|
||||
property id_token -> str;
|
||||
property session_state -> str;
|
||||
required link user -> User {
|
||||
on target delete delete source;
|
||||
};
|
||||
property createdAt -> datetime {
|
||||
default := datetime_current();
|
||||
};
|
||||
|
||||
constraint exclusive on ((.provider, .providerAccountId))
|
||||
}
|
||||
|
||||
type Session {
|
||||
required property sessionToken -> str {
|
||||
constraint exclusive;
|
||||
}
|
||||
required property userId := .user.id;
|
||||
required property expires -> datetime;
|
||||
required link user -> User {
|
||||
on target delete delete source;
|
||||
};
|
||||
property createdAt -> datetime {
|
||||
default := datetime_current();
|
||||
};
|
||||
}
|
||||
|
||||
type VerificationToken {
|
||||
required property identifier -> str;
|
||||
required property token -> str {
|
||||
constraint exclusive;
|
||||
}
|
||||
required property expires -> datetime;
|
||||
property createdAt -> datetime {
|
||||
default := datetime_current();
|
||||
};
|
||||
|
||||
constraint exclusive on ((.identifier, .token))
|
||||
}
|
||||
}
|
||||
|
||||
# Disable the application of access policies within access policies
|
||||
# themselves. This behavior will become the default in EdgeDB 3.0.
|
||||
# See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive
|
||||
using future nonrecursive_access_policies;
|
||||
2
apps/dev/nextjs/edgedb.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[edgedb]
|
||||
server-version = "2.6"
|
||||
@@ -15,11 +15,13 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@auth/edgedb-adapter": "workspace:*",
|
||||
"@auth/fauna-adapter": "workspace:*",
|
||||
"@auth/prisma-adapter": "workspace:*",
|
||||
"@auth/supabase-adapter": "workspace:*",
|
||||
"@auth/typeorm-adapter": "workspace:*",
|
||||
"@prisma/client": "^3",
|
||||
"edgedb": "^1.0.1",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.4.0",
|
||||
@@ -29,6 +31,7 @@
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edgedb/generate": "^0.0.4",
|
||||
"@playwright/test": "1.29.2",
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/react": "18.0.37",
|
||||
|
||||
@@ -39,7 +39,7 @@ import Yandex from "@auth/core/providers/yandex"
|
||||
import Vk from "@auth/core/providers/vk"
|
||||
import Wikimedia from "@auth/core/providers/wikimedia"
|
||||
import WorkOS from "@auth/core/providers/workos"
|
||||
|
||||
import ClickUp from '@auth/core/providers/click-up'
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
@@ -71,6 +71,12 @@ import WorkOS from "@auth/core/providers/workos"
|
||||
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
// })
|
||||
|
||||
// // EdgeDB
|
||||
// import { EdgeDBAdapter } from "@auth/edgedb-adapter"
|
||||
// import { createHttpClient } from "edgedb"
|
||||
// const client = createHttpClient()
|
||||
// const adapter = EdgeDBAdapter(client)
|
||||
|
||||
export const authConfig: AuthConfig = {
|
||||
// adapter,
|
||||
debug: process.env.NODE_ENV !== "production",
|
||||
@@ -131,6 +137,7 @@ export const authConfig: AuthConfig = {
|
||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
||||
ClickUp({ clientId: process.env.CLICK_UP_ID, clientSecret: process.env.CLICK_UP_SECRET })
|
||||
],
|
||||
// debug: process.env.NODE_ENV !== "production",
|
||||
}
|
||||
|
||||
5
apps/examples/nextjs/app/api/auth/[...nextauth]/route.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import NextAuth from "next-auth/next"
|
||||
import { config } from "auth"
|
||||
|
||||
const handler = NextAuth(config)
|
||||
export { handler as GET, handler as POST }
|
||||
294
apps/examples/nextjs/auth.ts
Normal file
@@ -0,0 +1,294 @@
|
||||
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next"
|
||||
import type { NextAuthOptions as NextAuthConfig } from "next-auth"
|
||||
import { getServerSession } from "next-auth"
|
||||
|
||||
import Apple from "next-auth/providers/apple"
|
||||
import Atlassian from "next-auth/providers/atlassian"
|
||||
import Auth0 from "next-auth/providers/auth0"
|
||||
import Authentik from "next-auth/providers/authentik"
|
||||
import AzureAD from "next-auth/providers/azure-ad"
|
||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||
import Battlenet from "next-auth/providers/battlenet"
|
||||
import Box from "next-auth/providers/box"
|
||||
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
|
||||
import Bungie from "next-auth/providers/bungie"
|
||||
import Cognito from "next-auth/providers/cognito"
|
||||
import Coinbase from "next-auth/providers/coinbase"
|
||||
import Discord from "next-auth/providers/discord"
|
||||
import Dropbox from "next-auth/providers/dropbox"
|
||||
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
|
||||
import Eveonline from "next-auth/providers/eveonline"
|
||||
import Facebook from "next-auth/providers/facebook"
|
||||
import Faceit from "next-auth/providers/faceit"
|
||||
import FortyTwoSchool from "next-auth/providers/42-school"
|
||||
import Foursquare from "next-auth/providers/foursquare"
|
||||
import Freshbooks from "next-auth/providers/freshbooks"
|
||||
import Fusionauth from "next-auth/providers/fusionauth"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
import Gitlab from "next-auth/providers/gitlab"
|
||||
import Google from "next-auth/providers/google"
|
||||
import Hubspot from "next-auth/providers/hubspot"
|
||||
import Instagram from "next-auth/providers/instagram"
|
||||
import Kakao from "next-auth/providers/kakao"
|
||||
import Keycloak from "next-auth/providers/keycloak"
|
||||
import Line from "next-auth/providers/line"
|
||||
import LinkedIn from "next-auth/providers/linkedin"
|
||||
import Mailchimp from "next-auth/providers/mailchimp"
|
||||
import Mailru from "next-auth/providers/mailru"
|
||||
import Medium from "next-auth/providers/medium"
|
||||
import Naver from "next-auth/providers/naver"
|
||||
import Netlify from "next-auth/providers/netlify"
|
||||
import Okta from "next-auth/providers/okta"
|
||||
import Onelogin from "next-auth/providers/onelogin"
|
||||
import Osso from "next-auth/providers/osso"
|
||||
import Osu from "next-auth/providers/osu"
|
||||
import Passage from "next-auth/providers/passage"
|
||||
import Patreon from "next-auth/providers/patreon"
|
||||
import Pinterest from "next-auth/providers/pinterest"
|
||||
import Pipedrive from "next-auth/providers/pipedrive"
|
||||
import Reddit from "next-auth/providers/reddit"
|
||||
import Salesforce from "next-auth/providers/salesforce"
|
||||
import Slack from "next-auth/providers/slack"
|
||||
import Spotify from "next-auth/providers/spotify"
|
||||
import Strava from "next-auth/providers/strava"
|
||||
import Todoist from "next-auth/providers/todoist"
|
||||
import Trakt from "next-auth/providers/trakt"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
import Twitter from "next-auth/providers/twitter"
|
||||
import UnitedEffects from "next-auth/providers/united-effects"
|
||||
import Vk from "next-auth/providers/vk"
|
||||
import Wikimedia from "next-auth/providers/wikimedia"
|
||||
import Wordpress from "next-auth/providers/wordpress"
|
||||
import WorkOS from "next-auth/providers/workos"
|
||||
import Yandex from "next-auth/providers/yandex"
|
||||
import Zitadel from "next-auth/providers/zitadel"
|
||||
import Zoho from "next-auth/providers/zoho"
|
||||
import Zoom from "next-auth/providers/zoom"
|
||||
|
||||
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
|
||||
declare module "next-auth/jwt" {
|
||||
interface JWT {
|
||||
/** The user's role. */
|
||||
userRole?: "admin"
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
// https://next-auth.js.org/configuration/providers/oauth
|
||||
providers: [
|
||||
Apple({ clientId: process.env.AUTH_APPLE_ID, clientSecret: process.env.AUTH_APPLE_SECRET }),
|
||||
Atlassian({ clientId: process.env.AUTH_ATLASSIAN_ID, clientSecret: process.env.AUTH_ATLASSIAN_SECRET }),
|
||||
Auth0({ clientId: process.env.AUTH_AUTH0_ID, clientSecret: process.env.AUTH_AUTH0_SECRET, issuer: process.env.AUTH_AUTH0_ISSUER }),
|
||||
Authentik({ clientId: process.env.AUTH_AUTHENTIK_ID, clientSecret: process.env.AUTH_AUTHENTIK_SECRET }),
|
||||
AzureAD({ clientId: process.env.AUTH_AZUREAD_ID, clientSecret: process.env.AUTH_AZUREAD_SECRET }),
|
||||
AzureB2C({ clientId: process.env.AUTH_AZUREB2C_ID, clientSecret: process.env.AUTH_AZUREB2C_SECRET }),
|
||||
Battlenet({ clientId: process.env.AUTH_BN_ID, clientSecret: process.env.AUTH_BN_SECRET, issuer: process.env.AUTH_BN_ISSUER }),
|
||||
Box({ clientId: process.env.AUTH_BOX_ID, clientSecret: process.env.AUTH_BOX_SECRET }),
|
||||
BoxyHQSAML({ clientId: process.env.AUTH_BOXYHQ_ID, clientSecret: process.env.AUTH_BOXYHQ_SECRET, issuer: process.env.AUTH_BOXYHQ_ISSUER }),
|
||||
Bungie({ clientId: process.env.AUTH_BUNGIE_ID, clientSecret: process.env.AUTH_BUNGIE_SECRET }),
|
||||
Cognito({ clientId: process.env.AUTH_COGNITO_ID, clientSecret: process.env.AUTH_COGNITO_SECRET }),
|
||||
Coinbase({ clientId: process.env.AUTH_COINBASE_ID, clientSecret: process.env.AUTH_COINBASE_SECRET }),
|
||||
Discord({ clientId: process.env.AUTH_DISCORD_ID, clientSecret: process.env.AUTH_DISCORD_SECRET }),
|
||||
Dropbox({ clientId: process.env.AUTH_DROPBOX_ID, clientSecret: process.env.AUTH_DROPBOX_SECRET }),
|
||||
DuendeIDS6({ clientId: process.env.AUTH_DUENDEIDS6_ID, clientSecret: process.env.AUTH_DUENDEIDS6_SECRET }),
|
||||
Eveonline({ clientId: process.env.AUTH_EVEONLINE_ID, clientSecret: process.env.AUTH_EVEONLINE_SECRET }),
|
||||
Facebook({ clientId: process.env.AUTH_FACEBOOK_ID, clientSecret: process.env.AUTH_FACEBOOK_SECRET }),
|
||||
Faceit({ clientId: process.env.AUTH_FACEIT_ID, clientSecret: process.env.AUTH_FACEIT_SECRET }),
|
||||
FortyTwoSchool({ clientId: process.env.AUTH_FORTYTWOSCHOOL_ID, clientSecret: process.env.AUTH_FORTYTWOSCHOOL_SECRET }),
|
||||
Foursquare({ clientId: process.env.AUTH_FOURSQUARE_ID, clientSecret: process.env.AUTH_FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.AUTH_FRESHBOOKS_ID, clientSecret: process.env.AUTH_FRESHBOOKS_SECRET }),
|
||||
Fusionauth({ clientId: process.env.AUTH_FUSIONAUTH_ID, clientSecret: process.env.AUTH_FUSIONAUTH_SECRET }),
|
||||
GitHub({ clientId: process.env.AUTH_GITHUB_ID, clientSecret: process.env.AUTH_GITHUB_SECRET }),
|
||||
Gitlab({ clientId: process.env.AUTH_GITLAB_ID, clientSecret: process.env.AUTH_GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET }),
|
||||
Hubspot({ clientId: process.env.AUTH_HUBSPOT_ID, clientSecret: process.env.AUTH_HUBSPOT_SECRET }),
|
||||
Instagram({ clientId: process.env.AUTH_INSTAGRAM_ID, clientSecret: process.env.AUTH_INSTAGRAM_SECRET }),
|
||||
Kakao({ clientId: process.env.AUTH_KAKAO_ID, clientSecret: process.env.AUTH_KAKAO_SECRET }),
|
||||
Keycloak({ clientId: process.env.AUTH_KEYCLOAK_ID, clientSecret: process.env.AUTH_KEYCLOAK_SECRET }),
|
||||
Line({ clientId: process.env.AUTH_LINE_ID, clientSecret: process.env.AUTH_LINE_SECRET }),
|
||||
LinkedIn({ clientId: process.env.AUTH_LINKEDIN_ID, clientSecret: process.env.AUTH_LINKEDIN_SECRET }),
|
||||
Mailchimp({ clientId: process.env.AUTH_MAILCHIMP_ID, clientSecret: process.env.AUTH_MAILCHIMP_SECRET }),
|
||||
Mailru({ clientId: process.env.AUTH_MAILRU_ID, clientSecret: process.env.AUTH_MAILRU_SECRET }),
|
||||
Medium({ clientId: process.env.AUTH_MEDIUM_ID, clientSecret: process.env.AUTH_MEDIUM_SECRET }),
|
||||
Naver({ clientId: process.env.AUTH_NAVER_ID, clientSecret: process.env.AUTH_NAVER_SECRET }),
|
||||
Netlify({ clientId: process.env.AUTH_NETLIFY_ID, clientSecret: process.env.AUTH_NETLIFY_SECRET }),
|
||||
Okta({ clientId: process.env.AUTH_OKTA_ID, clientSecret: process.env.AUTH_OKTA_SECRET }),
|
||||
Onelogin({ clientId: process.env.AUTH_ONELOGIN_ID, clientSecret: process.env.AUTH_ONELOGIN_SECRET }),
|
||||
Osso({ clientId: process.env.AUTH_OSSO_ID, clientSecret: process.env.AUTH_OSSO_SECRET, issuer: process.env.AUTH_OSSO_ISSUER }),
|
||||
Osu({ clientId: process.env.AUTH_OSU_ID, clientSecret: process.env.AUTH_OSU_SECRET }),
|
||||
Passage({ clientId: process.env.AUTH_PASSAGE_ID, clientSecret: process.env.AUTH_PASSAGE_SECRET, issuer: process.env.AUTH_PASSAGE_ISSUER }),
|
||||
Patreon({ clientId: process.env.AUTH_PATREON_ID, clientSecret: process.env.AUTH_PATREON_SECRET }),
|
||||
Pinterest({ clientId: process.env.AUTH_PINTEREST_ID, clientSecret: process.env.AUTH_PINTEREST_SECRET }),
|
||||
Pipedrive({ clientId: process.env.AUTH_PIPEDRIVE_ID, clientSecret: process.env.AUTH_PIPEDRIVE_SECRET }),
|
||||
Reddit({ clientId: process.env.AUTH_REDDIT_ID, clientSecret: process.env.AUTH_REDDIT_SECRET }),
|
||||
Salesforce({ clientId: process.env.AUTH_SALESFORCE_ID, clientSecret: process.env.AUTH_SALESFORCE_SECRET }),
|
||||
Slack({ clientId: process.env.AUTH_SLACK_ID, clientSecret: process.env.AUTH_SLACK_SECRET }),
|
||||
Spotify({ clientId: process.env.AUTH_SPOTIFY_ID, clientSecret: process.env.AUTH_SPOTIFY_SECRET }),
|
||||
Strava({ clientId: process.env.AUTH_STRAVA_ID, clientSecret: process.env.AUTH_STRAVA_SECRET }),
|
||||
Todoist({ clientId: process.env.AUTH_TODOIST_ID, clientSecret: process.env.AUTH_TODOIST_SECRET }),
|
||||
Trakt({ clientId: process.env.AUTH_TRAKT_ID, clientSecret: process.env.AUTH_TRAKT_SECRET }),
|
||||
Twitch({ clientId: process.env.AUTH_TWITCH_ID, clientSecret: process.env.AUTH_TWITCH_SECRET }),
|
||||
Twitter({ clientId: process.env.AUTH_TWITTER_ID, clientSecret: process.env.AUTH_TWITTER_SECRET, version: "2.0" }),
|
||||
UnitedEffects({ clientId: process.env.AUTH_UE_ID, clientSecret: process.env.AUTH_UE_SECRET, issuer: process.env.AUTH_UE_ISSUER }),
|
||||
Vk({ clientId: process.env.AUTH_VK_ID, clientSecret: process.env.AUTH_VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.AUTH_WIKIMEDIA_ID, clientSecret: process.env.AUTH_WIKIMEDIA_SECRET }),
|
||||
Wordpress({ clientId: process.env.AUTH_WORDPRESS_ID, clientSecret: process.env.AUTH_WORDPRESS_SECRET }),
|
||||
WorkOS({ clientId: process.env.AUTH_WORKOS_ID, clientSecret: process.env.AUTH_WORKOS_SECRET }),
|
||||
Yandex({ clientId: process.env.AUTH_YANDEX_ID, clientSecret: process.env.AUTH_YANDEX_SECRET }),
|
||||
Zitadel({ clientId: process.env.AUTH_ZITADEL_ID, clientSecret: process.env.AUTH_ZITADEL_SECRET }),
|
||||
Zoho({ clientId: process.env.AUTH_ZOHO_ID, clientSecret: process.env.AUTH_ZOHO_SECRET }),
|
||||
Zoom({ clientId: process.env.AUTH_ZOOM_ID, clientSecret: process.env.AUTH_ZOOM_SECRET }),
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token }) {
|
||||
token.userRole = "admin"
|
||||
return token
|
||||
},
|
||||
},
|
||||
} satisfies NextAuthConfig
|
||||
|
||||
// Helper function to get session without passing config every time
|
||||
// https://next-auth.js.org/configuration/nextjs#getserversession
|
||||
export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []) {
|
||||
return getServerSession(...args, config)
|
||||
}
|
||||
|
||||
// We recommend doing your own environment variable validation
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NEXTAUTH_SECRET: string
|
||||
|
||||
AUTH_APPLE_ID: string
|
||||
AUTH_APPLE_SECRET: string
|
||||
AUTH_ATLASSIAN_ID: string
|
||||
AUTH_ATLASSIAN_SECRET: string
|
||||
AUTH_AUTH0_ID: string
|
||||
AUTH_AUTH0_ISSUER: string
|
||||
AUTH_AUTH0_SECRET: string
|
||||
AUTH_AUTHENTIK_ID: string
|
||||
AUTH_AUTHENTIK_SECRET: string
|
||||
AUTH_AZUREAD_ID: string
|
||||
AUTH_AZUREAD_SECRET: string
|
||||
AUTH_AZUREB2C_ID: string
|
||||
AUTH_AZUREB2C_SECRET: string
|
||||
AUTH_BN_ID: string
|
||||
AUTH_BN_ISSUER: any
|
||||
AUTH_BN_SECRET: string
|
||||
AUTH_BOX_ID: string
|
||||
AUTH_BOX_SECRET: string
|
||||
AUTH_BOXYHQ_ID: string
|
||||
AUTH_BOXYHQ_ISSUER: string
|
||||
AUTH_BOXYHQ_SECRET: string
|
||||
AUTH_BUNGIE_ID: string
|
||||
AUTH_BUNGIE_SECRET: string
|
||||
AUTH_COGNITO_ID: string
|
||||
AUTH_COGNITO_SECRET: string
|
||||
AUTH_COINBASE_ID: string
|
||||
AUTH_COINBASE_SECRET: string
|
||||
AUTH_DISCORD_ID: string
|
||||
AUTH_DISCORD_SECRET: string
|
||||
AUTH_DROPBOX_ID: string
|
||||
AUTH_DROPBOX_SECRET: string
|
||||
AUTH_DUENDEIDS6_ID: string
|
||||
AUTH_DUENDEIDS6_SECRET: string
|
||||
AUTH_EVEONLINE_ID: string
|
||||
AUTH_EVEONLINE_SECRET: string
|
||||
AUTH_FACEBOOK_ID: string
|
||||
AUTH_FACEBOOK_SECRET: string
|
||||
AUTH_FACEIT_ID: string
|
||||
AUTH_FACEIT_SECRET: string
|
||||
AUTH_FORTYTWOSCHOOL_ID: string
|
||||
AUTH_FORTYTWOSCHOOL_SECRET: string
|
||||
AUTH_FOURSQUARE_ID: string
|
||||
AUTH_FOURSQUARE_SECRET: string
|
||||
AUTH_FRESHBOOKS_ID: string
|
||||
AUTH_FRESHBOOKS_SECRET: string
|
||||
AUTH_FUSIONAUTH_ID: string
|
||||
AUTH_FUSIONAUTH_SECRET: string
|
||||
AUTH_GITHUB_ID: string
|
||||
AUTH_GITHUB_SECRET: string
|
||||
AUTH_GITLAB_ID: string
|
||||
AUTH_GITLAB_SECRET: string
|
||||
AUTH_GOOGLE_ID: string
|
||||
AUTH_GOOGLE_SECRET: string
|
||||
AUTH_HUBSPOT_ID: string
|
||||
AUTH_HUBSPOT_SECRET: string
|
||||
AUTH_INSTAGRAM_ID: string
|
||||
AUTH_INSTAGRAM_SECRET: string
|
||||
AUTH_KAKAO_ID: string
|
||||
AUTH_KAKAO_SECRET: string
|
||||
AUTH_KEYCLOAK_ID: string
|
||||
AUTH_KEYCLOAK_SECRET: string
|
||||
AUTH_LINE_ID: string
|
||||
AUTH_LINE_SECRET: string
|
||||
AUTH_LINKEDIN_ID: string
|
||||
AUTH_LINKEDIN_SECRET: string
|
||||
AUTH_MAILCHIMP_ID: string
|
||||
AUTH_MAILCHIMP_SECRET: string
|
||||
AUTH_MAILRU_ID: string
|
||||
AUTH_MAILRU_SECRET: string
|
||||
AUTH_MEDIUM_ID: string
|
||||
AUTH_MEDIUM_SECRET: string
|
||||
AUTH_NAVER_ID: string
|
||||
AUTH_NAVER_SECRET: string
|
||||
AUTH_NETLIFY_ID: string
|
||||
AUTH_NETLIFY_SECRET: string
|
||||
AUTH_OKTA_ID: string
|
||||
AUTH_OKTA_SECRET: string
|
||||
AUTH_ONELOGIN_ID: string
|
||||
AUTH_ONELOGIN_SECRET: string
|
||||
AUTH_OSSO_ID: string
|
||||
AUTH_OSSO_ISSUER: string
|
||||
AUTH_OSSO_SECRET: string
|
||||
AUTH_OSU_ID: string
|
||||
AUTH_OSU_SECRET: string
|
||||
AUTH_PASSAGE_ID: string
|
||||
AUTH_PASSAGE_ISSUER: string
|
||||
AUTH_PASSAGE_SECRET: string
|
||||
AUTH_PATREON_ID: string
|
||||
AUTH_PATREON_SECRET: string
|
||||
AUTH_PINTEREST_ID: string
|
||||
AUTH_PINTEREST_SECRET: string
|
||||
AUTH_PIPEDRIVE_ID: string
|
||||
AUTH_PIPEDRIVE_SECRET: string
|
||||
AUTH_REDDIT_ID: string
|
||||
AUTH_REDDIT_SECRET: string
|
||||
AUTH_SALESFORCE_ID: string
|
||||
AUTH_SALESFORCE_SECRET: string
|
||||
AUTH_SLACK_ID: string
|
||||
AUTH_SLACK_SECRET: string
|
||||
AUTH_SPOTIFY_ID: string
|
||||
AUTH_SPOTIFY_SECRET: string
|
||||
AUTH_STRAVA_ID: string
|
||||
AUTH_STRAVA_SECRET: string
|
||||
AUTH_TODOIST_ID: string
|
||||
AUTH_TODOIST_SECRET: string
|
||||
AUTH_TRAKT_ID: string
|
||||
AUTH_TRAKT_SECRET: string
|
||||
AUTH_TWITCH_ID: string
|
||||
AUTH_TWITCH_SECRET: string
|
||||
AUTH_TWITTER_ID: string
|
||||
AUTH_TWITTER_SECRET: string
|
||||
AUTH_UE_ID: string
|
||||
AUTH_UE_ISSUER: string
|
||||
AUTH_UE_SECRET: string
|
||||
AUTH_VK_ID: string
|
||||
AUTH_VK_SECRET: string
|
||||
AUTH_WIKIMEDIA_ID: string
|
||||
AUTH_WIKIMEDIA_SECRET: string
|
||||
AUTH_WORDPRESS_ID: string
|
||||
AUTH_WORDPRESS_SECRET: string
|
||||
AUTH_WORKOS_ID: string
|
||||
AUTH_WORKOS_SECRET: string
|
||||
AUTH_YANDEX_ID: string
|
||||
AUTH_YANDEX_SECRET: string
|
||||
AUTH_ZITADEL_ID: string
|
||||
AUTH_ZITADEL_SECRET: string
|
||||
AUTH_ZOHO_ID: string
|
||||
AUTH_ZOHO_SECRET: string
|
||||
AUTH_ZOOM_ID: string
|
||||
AUTH_ZOOM_SECRET: string
|
||||
}
|
||||
}
|
||||
}
|
||||
10
apps/examples/nextjs/next-auth.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
import "next-auth/jwt"
|
||||
|
||||
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
|
||||
|
||||
declare module "next-auth/jwt" {
|
||||
interface JWT {
|
||||
/** The user's role. */
|
||||
userRole?: "admin"
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import FacebookProvider from "next-auth/providers/facebook"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
import TwitterProvider from "next-auth/providers/twitter"
|
||||
import Auth0Provider from "next-auth/providers/auth0"
|
||||
|
||||
// For more information on each option (and a full list of options) go to
|
||||
// https://next-auth.js.org/configuration/options
|
||||
export const authOptions: NextAuthOptions = {
|
||||
// https://next-auth.js.org/configuration/providers/oauth
|
||||
providers: [
|
||||
Auth0Provider({
|
||||
clientId: process.env.AUTH0_ID,
|
||||
clientSecret: process.env.AUTH0_SECRET,
|
||||
issuer: process.env.AUTH0_ISSUER,
|
||||
}),
|
||||
FacebookProvider({
|
||||
clientId: process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.FACEBOOK_SECRET,
|
||||
}),
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
TwitterProvider({
|
||||
clientId: process.env.TWITTER_ID,
|
||||
clientSecret: process.env.TWITTER_SECRET,
|
||||
version: "2.0",
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token }) {
|
||||
token.userRole = "admin"
|
||||
return token
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
@@ -1,14 +1,13 @@
|
||||
// This is an example of to protect an API route
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
import { auth } from "auth"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
const session = await auth(req, res)
|
||||
|
||||
if (session) {
|
||||
return res.send({
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// This is an example of how to access a session from an API route
|
||||
import { getServerSession } from "next-auth"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
import { auth } from "auth"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
const session = await auth(req, res)
|
||||
res.send(JSON.stringify(session, null, 2))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import { auth } from "auth"
|
||||
import Layout from "../components/layout"
|
||||
|
||||
import type { GetServerSidePropsContext } from "next"
|
||||
@@ -36,9 +35,5 @@ export default function ServerSidePage() {
|
||||
|
||||
// Export the `session` prop to use sessions with Server Side Rendering
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
return {
|
||||
props: {
|
||||
session: await getServerSession(context.req, context.res, authOptions),
|
||||
},
|
||||
}
|
||||
return { props: { session: await auth(context.req, context.res) } }
|
||||
}
|
||||
|
||||
18
apps/examples/nextjs/process.d.ts
vendored
@@ -1,18 +0,0 @@
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NEXTAUTH_URL: string
|
||||
NEXTAUTH_SECRET: string
|
||||
GITHUB_ID: string
|
||||
GITHUB_SECRET: string
|
||||
FACEBOOK_ID: string
|
||||
FACEBOOK_SECRET: string
|
||||
TWITTER_ID: string
|
||||
TWITTER_SECRET: string
|
||||
GOOGLE_ID: string
|
||||
GOOGLE_SECRET: string
|
||||
AUTH0_ID: string
|
||||
AUTH0_SECRET: string
|
||||
DESCOPE_ID: string
|
||||
DESCOPE_SECRET: string
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -13,14 +17,22 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"process.d.ts",
|
||||
"next-env.d.ts",
|
||||
"next-auth.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -182,7 +182,127 @@ export default async function listMovies(req, res) {
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO: SvelteKit
|
||||
|
||||
:::warning
|
||||
`@auth/sveltekit` is currently experimental. The API _will_ change in the future.
|
||||
:::
|
||||
|
||||
### Prerequisites
|
||||
|
||||
This tutorial assumes you have a SvelteKit application set up. If you don't, you can follow the [SvelteKit tutorial](https://kit.svelte.dev/docs/creating-a-project) to get started.
|
||||
|
||||
### Installing Auth.js
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @auth/core @auth/sveltekit
|
||||
```
|
||||
|
||||
### Create server hook
|
||||
|
||||
Create the following [Server hook](https://kit.svelte.dev/docs/hooks) file. This route contains the necessary configuration for Auth.js, as well as the dynamic route handler:
|
||||
|
||||
```ts title="src/hooks.server.ts"
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import GitHub from "@auth/core/providers/github"
|
||||
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
|
||||
*
|
||||
export const handle = SvelteKitAuth({
|
||||
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
|
||||
})
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
|
||||
|
||||
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
|
||||
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
|
||||
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
|
||||
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
|
||||
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
|
||||
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
|
||||
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
|
||||
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
|
||||
|
||||
can be handled by Auth.js. In this way, Auth.js stays in charge of the whole application's authentication request/response flow.
|
||||
|
||||
Auth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
|
||||
:::
|
||||
|
||||
### Adding environment variables
|
||||
|
||||
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
|
||||
|
||||
In your project root, create a `.env.local` file and add the `AUTH_SECRET` environment variable:
|
||||
|
||||
```title=".env.local"
|
||||
AUTH_SECRET="This is an example"
|
||||
```
|
||||
|
||||
`AUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
|
||||
|
||||
```
|
||||
$ openssl rand -base64 32
|
||||
```
|
||||
|
||||
or https://generate-secret.vercel.app/32 to generate a random value for it.
|
||||
|
||||
### Exposing the session via page store
|
||||
|
||||
Auth.js provides us a getSession, function to access the session data and status, to call from the `event.locals` variable. We can now just call it and add it to our `$page` store.
|
||||
|
||||
```ts
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
*
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
return {
|
||||
session: await event.locals.getSession()
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Consuming the session via page store
|
||||
|
||||
You can use the `$page.data.session` variable from anywhere on your page. Learn more about SvelteKit's page store in the [SvelteKit docs](https://learn.svelte.dev/tutorial/page-store).
|
||||
|
||||
```ts title="route/+page.svelte"
|
||||
<script>
|
||||
import { signIn, signOut } from '@auth/sveltekit/client'
|
||||
import { page } from '$app/stores'
|
||||
</script>
|
||||
|
||||
{#if $page.data.session?.user}
|
||||
<p>Signed in as {$page.data.session.user.email}</p>
|
||||
<button on:click={signOut}>Sign out</button>
|
||||
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
|
||||
{:else}
|
||||
<p>Not signed in.</p>
|
||||
<button on:click={() => signIn('github')}>Sign in</button>
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Protecting API Routes
|
||||
|
||||
To protect your API Routes (blocking unauthorized access to resources), you can use `locals.getSessions()` just like in the layouts file to know whether a session exists or not:
|
||||
|
||||
```ts title="routes/api/movies/+server.ts"
|
||||
import { json, error } from "@sveltejs/kit";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
export async function GET({ locals }: RequestEvent) {
|
||||
const session = await locals.getSession()
|
||||
if (!session?.user) {
|
||||
throw error(401, "You must sign in to view movies.");
|
||||
}
|
||||
|
||||
return json({
|
||||
movies: [
|
||||
{ title: "Alien vs Predator", id: 1 },
|
||||
{ title: "Reservoir Dogs", id: 2 },
|
||||
]
|
||||
})
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO: SolidStart
|
||||
@@ -314,7 +434,24 @@ $ npm run next dev
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
|
||||
```ts title="src/hooks.server.ts"
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import GitHub from "@auth/core/providers/github"
|
||||
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
|
||||
*
|
||||
export const handle = SvelteKitAuth({
|
||||
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
|
||||
})
|
||||
```
|
||||
|
||||
Great! We're now ready to run our application locally. Start the Svelte app by running on your terminal the following command and navigating to [`http://localhost:5173`](http://localhost:5173):
|
||||
|
||||
```
|
||||
$ npm run vite dev
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
|
||||
@@ -7,11 +7,7 @@ The Credentials provider allows you to handle signing in with arbitrary credenti
|
||||
|
||||
It is intended to support use cases where you have an existing system you need to authenticate users against.
|
||||
|
||||
It comes with the constraint that users authenticated in this manner are not persisted in the database, and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
|
||||
|
||||
:::warning
|
||||
The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
|
||||
:::
|
||||
It comes with the constraint that users authenticated in this manner are not persisted in the database by default, and consequently, that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
|
||||
|
||||
## Options
|
||||
|
||||
@@ -23,17 +19,31 @@ You can override any of the options to suit your own use case.
|
||||
|
||||
## Example - Username / Password
|
||||
|
||||
The Credentials provider is specified like other providers, except that you need to define a handler for `authorize()` that accepts credentials submitted via HTTP POST as input and returns either:
|
||||
:::caution
|
||||
The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks of the username-password model.
|
||||
|
||||
1. A `user` object, which indicates the credentials are valid.
|
||||
OAuth providers spend significant amounts of money, time, and engineering effort to build:
|
||||
|
||||
If you return an object it will be persisted to the JSON Web Token and the user will be signed in, unless a custom `signIn()` callback is configured that subsequently rejects it.
|
||||
- bot-protection
|
||||
- rate-limiting
|
||||
- password management
|
||||
- data security
|
||||
|
||||
2. If you return `null` then an error will be displayed advising the user to check their details.
|
||||
and much more for authentication solutions. It is likely that your application would benefit from leveraging these battle-tested solutions rather than try to rebuild them from scratch.
|
||||
|
||||
3. If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
|
||||
If you'd still like to build password-based authentication for your application despite these risks, Auth.js gives you full control to do so.
|
||||
|
||||
The Credentials provider's `authorize()` method also provides the request object as the second parameter (see the example below).
|
||||
:::
|
||||
|
||||
The Credentials provider is specified like other providers, except that you need to define a handler for `authorize()` that accepts credentials submitted via HTTP POST as input and returns a `user` object, which indicates the credentials are valid.
|
||||
|
||||
If you return an object it will be persisted to the JSON Web Token and the user will be signed in (unless a custom `signIn()` callback is configured that subsequently rejects it).
|
||||
|
||||
- If you return `null` then an error will be displayed advising the user to check their details.
|
||||
|
||||
- If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
|
||||
|
||||
The Credentials provider's `authorize()` method also provides the request object as the second parameter. Here's an example that handles these concerns.
|
||||
|
||||
```js title="auth.js"
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
@@ -69,7 +79,50 @@ providers: [
|
||||
...
|
||||
```
|
||||
|
||||
See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token.
|
||||
See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token. For example, you can add additional information to the token by returning an object from the `jwt()` callback:
|
||||
|
||||
```js
|
||||
callbacks: {
|
||||
async jwt(token, user, account, profile, isNewUser) {
|
||||
if (user) {
|
||||
token.id = user.id
|
||||
}
|
||||
return token
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using a database
|
||||
|
||||
You can also use the `authorize()` callback to interact with your database to regain some of the functionality from [more powerful providers](reference/core/providers):
|
||||
|
||||
```js
|
||||
...
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
...
|
||||
async authorize(credentials, req) {
|
||||
let user = null
|
||||
|
||||
const saltedPasswordToCheck = passwordToSalt(credentials.password)
|
||||
user = await getUserFromDb(credentials.username, credentials.password)
|
||||
|
||||
if (!user) {
|
||||
const saltedPassword = passwordToSalt(credentials.password)
|
||||
user = await addUserToDb(credentials.username, saltedPassword)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User was not found and could not be created.")
|
||||
}
|
||||
|
||||
return user
|
||||
|
||||
}
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
## Example - Web3 / Signin With Ethereum
|
||||
|
||||
@@ -78,8 +131,8 @@ The credentials provider can also be used to integrate with a service like [Sign
|
||||
For more information, check out the links below:
|
||||
|
||||
- [Tutorial](https://docs.login.xyz/integrations/Auth.js)
|
||||
- [Example App Repo](https://github.com/spruceid/siwe-next-auth-example).
|
||||
- [Example App Demo](https://siwe-next-auth-example2.vercel.app/).
|
||||
- [Example App Repo](https://github.com/spruceid/siwe-next-auth-example)
|
||||
- [Example App Demo](https://siwe-next-auth-example2.vercel.app/)
|
||||
|
||||
## Multiple providers
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ title: Overview
|
||||
Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
|
||||
|
||||
<div class="adapter-card-list">
|
||||
<a href="/reference/adapter/edgedb" class="adapter-card">
|
||||
<img src="/img/adapters/edgedb.svg" width="30" />
|
||||
<h4 class="adapter-card__title">EdgeDB Adapter</h4>
|
||||
</a>
|
||||
<a href="/reference/adapter/dgraph" class="adapter-card">
|
||||
<img src="/img/adapters/dgraph.png" width="30" />
|
||||
<h4 class="adapter-card__title">Dgraph Adapter</h4>
|
||||
|
||||
@@ -282,6 +282,7 @@ const docusaurusConfig = {
|
||||
...(process.env.TYPEDOC_SKIP_ADAPTERS
|
||||
? []
|
||||
: [
|
||||
typedocAdapter("EdgeDb"),
|
||||
typedocAdapter("Dgraph"),
|
||||
typedocAdapter("Drizzle"),
|
||||
typedocAdapter("DynamoDB"),
|
||||
|
||||
4
docs/static/img/adapters/edgedb.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg style="color:#1F8AED" width="140" height="114" viewBox="0 0 140 114" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.58 56.966C118.58 66.6274 114.706 68.3592 110.377 68.3592H101.171V45.5728H110.377C114.706 45.5728 118.58 47.3046 118.58 56.966ZM113.887 56.966C113.887 50.2212 111.836 49.9022 108.874 49.9022H106.003V64.0298H108.874C111.836 64.0298 113.887 63.7108 113.887 56.966ZM65.1234 68.3592V45.5728H79.6155V49.9022H69.9541V54.5507H77.2458V58.8345H69.9541V64.0298H79.6155V68.3592H65.1234ZM87.7282 113.932H92.5589V0H87.7282V113.932ZM128.242 58.242V64.0298H132.252C134.758 64.0298 135.396 62.3892 135.396 61.1587C135.396 60.2017 134.941 58.242 131.523 58.242H128.242ZM128.242 49.9022V54.2317H131.523C133.391 54.2317 134.485 53.4113 134.485 52.0442C134.485 50.677 133.391 49.9022 131.523 49.9022H128.242ZM123.411 45.5728H132.708C137.585 45.5728 139.043 48.9908 139.043 51.4517C139.043 53.7304 137.585 55.371 136.582 55.8267C139.499 57.2395 140 60.1105 140 61.5689C140 63.4829 139.043 68.3592 132.708 68.3592H123.411V45.5728ZM37.3702 56.966C37.3702 66.6274 33.4966 68.3592 29.1671 68.3592H19.9614V45.5728H29.1671C33.4966 45.5728 37.3702 47.3046 37.3702 56.966ZM51.5885 64.2121C54.0495 64.2121 55.3255 63.3918 55.7812 62.8449V60.3384H51.862V56.4191H59.6549V65.4881C58.9713 66.5363 55.2344 68.5871 51.8164 68.5871C46.211 68.5871 41.4714 66.3996 41.4714 56.7381C41.4714 47.0767 46.2565 45.345 50.5859 45.345C57.3763 45.345 59.0625 48.8996 59.6094 52.0442L55.5989 52.9556C55.3711 51.4973 54.095 49.6744 51.1784 49.6744C48.2162 49.6744 46.1654 49.9934 46.1654 56.7381C46.1654 63.4829 48.3073 64.2121 51.5885 64.2121ZM32.6756 56.966C32.6756 50.2212 30.6248 49.9022 27.6626 49.9022H24.7915V64.0298H27.6626C30.6248 64.0298 32.6756 63.7108 32.6756 56.966ZM0 68.3592V45.5728H14.4921V49.9022H4.83072V54.5507H12.1224V58.8345H4.83072V64.0298H14.4921V68.3592H0Z" fill="currentColor">
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
27
docs/static/img/providers/click-up.svg
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<svg width="185" height="185" viewBox="0 0 185 185" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.8789 105.714L69.3974 95.3593C76.5762 104.732 84.1998 109.051 92.6948 109.051C101.143 109.051 108.557 104.781 115.414 95.4832L129.119 105.59C119.232 118.996 106.932 126.079 92.6948 126.079C78.5049 126.079 66.0907 119.046 55.8789 105.714Z" fill="url(#paint0_linear)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.6491 60.7078L68.5883 81.4406L57.4727 68.5407L92.6969 38.1885L127.647 68.5644L116.477 81.417L92.6491 60.7078Z" fill="url(#paint1_linear)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="0" y="0" width="185" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dy="10"/>
|
||||
<feGaussianBlur stdDeviation="15"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.117647 0 0 0 0 0.211765 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear" x1="55.8789" y1="116.251" x2="129.119" y2="116.251" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8930FD"/>
|
||||
<stop offset="1" stop-color="#49CCF9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="57.4727" y1="67.6025" x2="127.647" y2="67.6025" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF02F0"/>
|
||||
<stop offset="1" stop-color="#FFC800"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
3
docs/static/img/providers/dribbble-dark.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16704 0 0 7.16704 0 16C0 24.833 7.16704 32 16 32C24.8156 32 32 24.833 32 16C32 7.16704 24.8156 0 16 0ZM26.5683 7.37526C28.4772 9.70064 29.6226 12.6681 29.6573 15.8785C29.2061 15.7917 24.6941 14.872 20.1475 15.4447C20.0434 15.2191 19.9566 14.9761 19.8525 14.7332C19.5748 14.0738 19.2625 13.397 18.9501 12.7549C23.9827 10.7072 26.2733 7.75706 26.5683 7.37526ZM16 2.36009C19.4707 2.36009 22.6464 3.6616 25.0586 5.7961C24.8156 6.14317 22.7505 8.9024 17.8916 10.7245C15.6529 6.61171 13.1714 3.24512 12.7896 2.72451C13.8134 2.48156 14.8894 2.36009 16 2.36009ZM10.1866 3.64426C10.551 4.13014 12.9805 7.51411 15.2538 11.5401C8.86768 13.2408 3.22778 13.2061 2.62039 13.2061C3.50541 8.97181 6.36877 5.44902 10.1866 3.64426ZM2.32538 16.0173C2.32538 15.8785 2.32538 15.7397 2.32538 15.6009C2.9154 15.6182 9.54448 15.705 16.3644 13.6573C16.7636 14.4208 17.128 15.2017 17.475 15.9827C17.3015 16.0347 17.1106 16.0868 16.9371 16.1388C9.89155 18.4122 6.14317 24.6247 5.83082 25.1453C3.6616 22.7332 2.32538 19.5228 2.32538 16.0173ZM16 29.6746C12.8417 29.6746 9.92624 28.5987 7.61821 26.7939C7.86118 26.2907 10.6378 20.9458 18.3427 18.256C18.3774 18.2386 18.3948 18.2386 18.4295 18.2212C20.3557 23.2017 21.1367 27.3839 21.3449 28.5813C19.6963 29.2928 17.8916 29.6746 16 29.6746ZM23.6182 27.3319C23.4794 26.4989 22.7506 22.5076 20.9631 17.5965C25.2495 16.9197 28.9978 18.0304 29.4664 18.1866C28.8764 21.987 26.6898 25.2668 23.6182 27.3319Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
3
docs/static/img/providers/dribbble.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16704 0 0 7.16704 0 16C0 24.833 7.16704 32 16 32C24.8156 32 32 24.833 32 16C32 7.16704 24.8156 0 16 0ZM26.5683 7.37526C28.4772 9.70064 29.6226 12.6681 29.6573 15.8785C29.2061 15.7917 24.6941 14.872 20.1475 15.4447C20.0434 15.2191 19.9566 14.9761 19.8525 14.7332C19.5748 14.0738 19.2625 13.397 18.9501 12.7549C23.9827 10.7072 26.2733 7.75706 26.5683 7.37526ZM16 2.36009C19.4707 2.36009 22.6464 3.6616 25.0586 5.7961C24.8156 6.14317 22.7505 8.9024 17.8916 10.7245C15.6529 6.61171 13.1714 3.24512 12.7896 2.72451C13.8134 2.48156 14.8894 2.36009 16 2.36009ZM10.1866 3.64426C10.551 4.13014 12.9805 7.51411 15.2538 11.5401C8.86768 13.2408 3.22778 13.2061 2.62039 13.2061C3.50541 8.97181 6.36877 5.44902 10.1866 3.64426ZM2.32538 16.0173C2.32538 15.8785 2.32538 15.7397 2.32538 15.6009C2.9154 15.6182 9.54448 15.705 16.3644 13.6573C16.7636 14.4208 17.128 15.2017 17.475 15.9827C17.3015 16.0347 17.1106 16.0868 16.9371 16.1388C9.89155 18.4122 6.14317 24.6247 5.83082 25.1453C3.6616 22.7332 2.32538 19.5228 2.32538 16.0173ZM16 29.6746C12.8417 29.6746 9.92624 28.5987 7.61821 26.7939C7.86118 26.2907 10.6378 20.9458 18.3427 18.256C18.3774 18.2386 18.3948 18.2386 18.4295 18.2212C20.3557 23.2017 21.1367 27.3839 21.3449 28.5813C19.6963 29.2928 17.8916 29.6746 16 29.6746ZM23.6182 27.3319C23.4794 26.4989 22.7506 22.5076 20.9631 17.5965C25.2495 16.9197 28.9978 18.0304 29.4664 18.1866C28.8764 21.987 26.6898 25.2668 23.6182 27.3319Z" fill="#EA4C89"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
10
docs/static/img/providers/mastodon.svg
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="75" height="79" viewBox="0 0 75 79" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M73.8393 17.4898C72.6973 9.00165 65.2994 2.31235 56.5296 1.01614C55.05 0.797115 49.4441 0 36.4582 0H36.3612C23.3717 0 20.585 0.797115 19.1054 1.01614C10.5798 2.27644 2.79399 8.28712 0.904997 16.8758C-0.00358524 21.1056 -0.100549 25.7949 0.0682394 30.0965C0.308852 36.2651 0.355538 42.423 0.91577 48.5665C1.30307 52.6474 1.97872 56.6957 2.93763 60.6812C4.73325 68.042 12.0019 74.1676 19.1233 76.6666C26.7478 79.2728 34.9474 79.7055 42.8039 77.9162C43.6682 77.7151 44.5217 77.4817 45.3645 77.216C47.275 76.6092 49.5123 75.9305 51.1571 74.7385C51.1797 74.7217 51.1982 74.7001 51.2112 74.6753C51.2243 74.6504 51.2316 74.6229 51.2325 74.5948V68.6416C51.2321 68.6154 51.2259 68.5896 51.2142 68.5661C51.2025 68.5426 51.1858 68.522 51.1651 68.5058C51.1444 68.4896 51.1204 68.4783 51.0948 68.4726C51.0692 68.4669 51.0426 68.467 51.0171 68.4729C45.9835 69.675 40.8254 70.2777 35.6502 70.2682C26.7439 70.2682 24.3486 66.042 23.6626 64.2826C23.1113 62.762 22.7612 61.1759 22.6212 59.5646C22.6197 59.5375 22.6247 59.5105 22.6357 59.4857C22.6466 59.4609 22.6633 59.4391 22.6843 59.422C22.7053 59.4048 22.73 59.3929 22.7565 59.3871C22.783 59.3813 22.8104 59.3818 22.8367 59.3886C27.7864 60.5826 32.8604 61.1853 37.9522 61.1839C39.1768 61.1839 40.3978 61.1839 41.6224 61.1516C46.7435 61.008 52.1411 60.7459 57.1796 59.7621C57.3053 59.7369 57.431 59.7154 57.5387 59.6831C65.4861 58.157 73.0493 53.3672 73.8178 41.2381C73.8465 40.7606 73.9184 36.2364 73.9184 35.7409C73.9219 34.0569 74.4606 23.7949 73.8393 17.4898Z" fill="url(#paint0_linear_549_34)"/>
|
||||
<path d="M61.2484 27.0263V48.114H52.8916V27.6475C52.8916 23.3388 51.096 21.1413 47.4437 21.1413C43.4287 21.1413 41.4177 23.7409 41.4177 28.8755V40.0782H33.1111V28.8755C33.1111 23.7409 31.0965 21.1413 27.0815 21.1413C23.4507 21.1413 21.6371 23.3388 21.6371 27.6475V48.114H13.2839V27.0263C13.2839 22.7176 14.384 19.2946 16.5843 16.7572C18.8539 14.2258 21.8311 12.926 25.5264 12.926C29.8036 12.926 33.0357 14.5705 35.1905 17.8559L37.2698 21.346L39.3527 17.8559C41.5074 14.5705 44.7395 12.926 49.0095 12.926C52.7013 12.926 55.6784 14.2258 57.9553 16.7572C60.1531 19.2922 61.2508 22.7152 61.2484 27.0263Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_549_34" x1="37.0692" y1="0" x2="37.0692" y2="79" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#6364FF"/>
|
||||
<stop offset="1" stop-color="#563ACC"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
7
docs/static/img/providers/tiktok-dark.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" width="64px" height="64px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
14
docs/static/img/providers/tiktok.svg
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-label="TikTok" role="img" viewBox="0 0 512.00 512.00" width="64px" height="64px" fill="#000000" stroke="#000000" stroke-width="0.00512">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="1.024"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
|
||||
<rect rx="15%" height="512" width="512" fill="#ffffff"/>
|
||||
|
||||
<defs>
|
||||
|
After Width: | Height: | Size: 924 B |
@@ -249,6 +249,7 @@
|
||||
{
|
||||
"files": [
|
||||
"apps/dev/nextjs/pages/api/auth/[...nextauth].ts",
|
||||
"apps/examples/nextjs/auth.ts",
|
||||
"docs/{sidebars,docusaurus.config}.js"
|
||||
],
|
||||
"options": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
@@ -57,4 +57,4 @@
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ export interface DynamoDBAdapterOptions {
|
||||
*
|
||||
* const config: DynamoDBClientConfig = {
|
||||
* credentials: {
|
||||
* accessKeyId: process.env.NEXT_AUTH_AWS_ACCESS_KEY as string,
|
||||
* secretAccessKey: process.env.NEXT_AUTH_AWS_SECRET_KEY as string,
|
||||
* accessKeyId: process.env.NEXT_AUTH_AWS_ACCESS_KEY,
|
||||
* secretAccessKey: process.env.NEXT_AUTH_AWS_SECRET_KEY,
|
||||
* },
|
||||
* region: process.env.NEXT_AUTH_AWS_REGION,
|
||||
* };
|
||||
|
||||
28
packages/adapter-edgedb/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://authjs.dev" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
||||
</a>
|
||||
<a href="https://www.edgedb.com/" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/adapters/edgedb.svg"/>
|
||||
</a>
|
||||
<h3 align="center"><b>Edge DB Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||
<p align="center" style="align: center;">
|
||||
<a href="https://npm.im/@auth/edgedb-adapter">
|
||||
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
||||
</a>
|
||||
<a href="https://npm.im/@auth/edgedb-adapter">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/@auth/edgedb-adapter?color=green&label=@auth/edgedb-adapter&style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmtrends.com/@auth/edgedb-adapter">
|
||||
<img src="https://img.shields.io/npm/dm/@auth/edgedb-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
<a href="https://github.com/nextauthjs/next-auth/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
|
||||
</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/edgedb).
|
||||
4
packages/adapter-edgedb/logo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg style="color:#1F8AED" width="140" height="114" viewBox="0 0 140 114" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.58 56.966C118.58 66.6274 114.706 68.3592 110.377 68.3592H101.171V45.5728H110.377C114.706 45.5728 118.58 47.3046 118.58 56.966ZM113.887 56.966C113.887 50.2212 111.836 49.9022 108.874 49.9022H106.003V64.0298H108.874C111.836 64.0298 113.887 63.7108 113.887 56.966ZM65.1234 68.3592V45.5728H79.6155V49.9022H69.9541V54.5507H77.2458V58.8345H69.9541V64.0298H79.6155V68.3592H65.1234ZM87.7282 113.932H92.5589V0H87.7282V113.932ZM128.242 58.242V64.0298H132.252C134.758 64.0298 135.396 62.3892 135.396 61.1587C135.396 60.2017 134.941 58.242 131.523 58.242H128.242ZM128.242 49.9022V54.2317H131.523C133.391 54.2317 134.485 53.4113 134.485 52.0442C134.485 50.677 133.391 49.9022 131.523 49.9022H128.242ZM123.411 45.5728H132.708C137.585 45.5728 139.043 48.9908 139.043 51.4517C139.043 53.7304 137.585 55.371 136.582 55.8267C139.499 57.2395 140 60.1105 140 61.5689C140 63.4829 139.043 68.3592 132.708 68.3592H123.411V45.5728ZM37.3702 56.966C37.3702 66.6274 33.4966 68.3592 29.1671 68.3592H19.9614V45.5728H29.1671C33.4966 45.5728 37.3702 47.3046 37.3702 56.966ZM51.5885 64.2121C54.0495 64.2121 55.3255 63.3918 55.7812 62.8449V60.3384H51.862V56.4191H59.6549V65.4881C58.9713 66.5363 55.2344 68.5871 51.8164 68.5871C46.211 68.5871 41.4714 66.3996 41.4714 56.7381C41.4714 47.0767 46.2565 45.345 50.5859 45.345C57.3763 45.345 59.0625 48.8996 59.6094 52.0442L55.5989 52.9556C55.3711 51.4973 54.095 49.6744 51.1784 49.6744C48.2162 49.6744 46.1654 49.9934 46.1654 56.7381C46.1654 63.4829 48.3073 64.2121 51.5885 64.2121ZM32.6756 56.966C32.6756 50.2212 30.6248 49.9022 27.6626 49.9022H24.7915V64.0298H27.6626C30.6248 64.0298 32.6756 63.7108 32.6756 56.966ZM0 68.3592V45.5728H14.4921V49.9022H4.83072V54.5507H12.1224V58.8345H4.83072V64.0298H14.4921V68.3592H0Z" fill="currentColor">
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
55
packages/adapter-edgedb/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@auth/edgedb-adapter",
|
||||
"version": "0.2.0",
|
||||
"description": "EdgeDB adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nextauthjs/next-auth/issues"
|
||||
},
|
||||
"author": "Bruno Crosier",
|
||||
"type": "module",
|
||||
"types": "./index.d.ts",
|
||||
"files": [
|
||||
"*.d.ts*",
|
||||
"*.js",
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./index.js"
|
||||
}
|
||||
},
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
"next.js",
|
||||
"oauth",
|
||||
"edgedb"
|
||||
],
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "jest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"edgedb": "^1.0.1",
|
||||
"@auth/core": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@auth/adapter-test": "workspace:^0.0.0",
|
||||
"@auth/tsconfig": "workspace:^0.0.0",
|
||||
"jest": "^27.4.3",
|
||||
"@auth/core": "workspace:*",
|
||||
"typescript": "^4.7.4",
|
||||
"edgedb": "^1.0.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
615
packages/adapter-edgedb/src/index.ts
Normal file
@@ -0,0 +1,615 @@
|
||||
/**
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
|
||||
* <p style={{fontWeight: "normal"}}>Official <a href="https://www.edgedb.com/">Edge DB</a> adapter for Auth.js / NextAuth.js.</p>
|
||||
* <a href="https://www.edgedb.com/">
|
||||
* <img style={{display: "block"}} src="/img/adapters/edgedb.svg" width="38" />
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install edgedb @auth/edgedb-adapter
|
||||
* npm install @edgedb/generate --save-dev
|
||||
* ```
|
||||
*
|
||||
* @module @auth/edgedb-adapter
|
||||
*/
|
||||
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
VerificationToken,
|
||||
} from "@auth/core/adapters"
|
||||
import type { Client } from "edgedb"
|
||||
|
||||
/**
|
||||
*
|
||||
* To use this Adapter, you need to install `edgedb`, `@edgedb/generate`, and the separate `@auth/edgedb-adapter` package:
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install edgedb @auth/edgedb-adapter
|
||||
* npm install @edgedb/generate --save-dev
|
||||
* ```
|
||||
*
|
||||
* ## Installation
|
||||
*
|
||||
* First, ensure you have the EdgeDB CLI installed.
|
||||
*
|
||||
* Follow the instructions below, or read the [EdgeDB quickstart](https://www.edgedb.com/docs/intro/quickstart) to install the EdgeDB CLI and initialize a project
|
||||
*
|
||||
* ### Linux or macOS
|
||||
* ```bash
|
||||
* curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh
|
||||
* ```
|
||||
*
|
||||
* ### Windows
|
||||
* ```powershell
|
||||
* iwr https://ps1.edgedb.com -useb | iex
|
||||
* ```
|
||||
*
|
||||
* Check that the CLI is available with the `edgedb --version` command. If you get a `Command not found` error, you may need to open a new terminal window before the `edgedb` command is available.
|
||||
*
|
||||
* Once the CLI is installed, initialize a project from the application’s root directory. You’ll be presented with a series of prompts.
|
||||
*
|
||||
* ```bash
|
||||
* edgedb project init
|
||||
* ```
|
||||
*
|
||||
* This process will spin up an EdgeDB instance and [“link”](https://www.edgedb.com/docs/cli/edgedb_instance/edgedb_instance_link#edgedb-instance-link) it with your current directory. As long as you’re inside that directory, CLI commands and client libraries will be able to connect to the linked instance automatically, without additional configuration.
|
||||
*
|
||||
* ## Setup
|
||||
*
|
||||
* ### NextAuth.js configuration
|
||||
*
|
||||
* Configure your NextAuth.js to use the EdgeDB Adapter:
|
||||
*
|
||||
* ```javascript title="pages/api/auth/[...nextauth].js"
|
||||
* import NextAuth from "next-auth"
|
||||
* import GoogleProvider from "next-auth/providers/google"
|
||||
* import { EdgeDBAdapter } from "@auth/edgedb-adapter"
|
||||
* import { createClient } from "edgedb"
|
||||
*
|
||||
* const client = createClient()
|
||||
*
|
||||
* export default NextAuth({
|
||||
* adapter: EdgeDBAdapter(client),
|
||||
* providers: [
|
||||
* GoogleProvider({
|
||||
* clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ### Create the EdgeDB schema
|
||||
*
|
||||
* Replace the contents of the auto-generated file in `dbschema/default.esdl` with the following:
|
||||
*
|
||||
* > This schema is adapted for use in EdgeDB and based upon our main [schema](/adapters/models)
|
||||
*
|
||||
* ```json title="default.esdl"
|
||||
* module default {
|
||||
* type User {
|
||||
* property name -> str;
|
||||
* required property email -> str {
|
||||
* constraint exclusive;
|
||||
* }
|
||||
* property emailVerified -> datetime;
|
||||
* property image -> str;
|
||||
* multi link accounts := .<user[is Account];
|
||||
* multi link sessions := .<user[is Session];
|
||||
* property createdAt -> datetime {
|
||||
* default := datetime_current();
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* type Account {
|
||||
* required property userId := .user.id;
|
||||
* required property type -> str;
|
||||
* required property provider -> str;
|
||||
* required property providerAccountId -> str {
|
||||
* constraint exclusive;
|
||||
* };
|
||||
* property refresh_token -> str;
|
||||
* property access_token -> str;
|
||||
* property expires_at -> int64;
|
||||
* property token_type -> str;
|
||||
* property scope -> str;
|
||||
* property id_token -> str;
|
||||
* property session_state -> str;
|
||||
* required link user -> User {
|
||||
* on target delete delete source;
|
||||
* };
|
||||
* property createdAt -> datetime {
|
||||
* default := datetime_current();
|
||||
* };
|
||||
*
|
||||
* constraint exclusive on ((.provider, .providerAccountId))
|
||||
* }
|
||||
*
|
||||
* type Session {
|
||||
* required property sessionToken -> str {
|
||||
* constraint exclusive;
|
||||
* }
|
||||
* required property userId := .user.id;
|
||||
* required property expires -> datetime;
|
||||
* required link user -> User {
|
||||
* on target delete delete source;
|
||||
* };
|
||||
* property createdAt -> datetime {
|
||||
* default := datetime_current();
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* type VerificationToken {
|
||||
* required property identifier -> str;
|
||||
* required property token -> str {
|
||||
* constraint exclusive;
|
||||
* }
|
||||
* required property expires -> datetime;
|
||||
* property createdAt -> datetime {
|
||||
* default := datetime_current();
|
||||
* };
|
||||
*
|
||||
* constraint exclusive on ((.identifier, .token))
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* # Disable the application of access policies within access policies
|
||||
* # themselves. This behavior will become the default in EdgeDB 3.0.
|
||||
* # See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive
|
||||
* using future nonrecursive_access_policies;
|
||||
* ```
|
||||
*
|
||||
* ### Migrate the database schema
|
||||
*
|
||||
* Create a migration
|
||||
*
|
||||
* ```
|
||||
* edgedb migration create
|
||||
* ```
|
||||
*
|
||||
* Apply the migration
|
||||
*
|
||||
* ```
|
||||
* edgedb migrate
|
||||
* ```
|
||||
*
|
||||
* To learn more about [EdgeDB migrations](https://www.edgedb.com/docs/intro/migrations#generate-a-migration), check out the [Migrations docs](https://www.edgedb.com/docs/intro/migrations).
|
||||
*
|
||||
* ### Generate the query builder
|
||||
*
|
||||
* ```npm2yarn2pnpm
|
||||
* npx @edgedb/generate edgeql-js
|
||||
* ```
|
||||
*
|
||||
* This will generate the [query builder](https://www.edgedb.com/docs/clients/js/querybuilder) so that you can write fully typed EdgeQL queries with TypeScript in a code-first way.
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* ```ts
|
||||
* const query = e.select(e.User, () => ({
|
||||
* id: true,
|
||||
* email: true,
|
||||
* emailVerified: true,
|
||||
* name: true,
|
||||
* image: true,
|
||||
* filter_single: { email: 'johndoe@example.com' },
|
||||
* }));
|
||||
*
|
||||
* return await query.run(client);
|
||||
*
|
||||
* // Return type:
|
||||
* // {
|
||||
* // id: string;
|
||||
* // email: string;
|
||||
* // emailVerified: Date | null;
|
||||
* // image: string | null;
|
||||
* // name: string | null;
|
||||
* // } | null
|
||||
*
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## Deploying
|
||||
*
|
||||
* ### Deploy EdgeDB
|
||||
*
|
||||
* First deploy an EdgeDB instance on your preferred cloud provider:
|
||||
*
|
||||
* [AWS](https://www.edgedb.com/docs/guides/deployment/aws_aurora_ecs)
|
||||
*
|
||||
* [Google Cloud](https://www.edgedb.com/docs/guides/deployment/gcp)
|
||||
*
|
||||
* [Azure](https://www.edgedb.com/docs/guides/deployment/azure_flexibleserver)
|
||||
*
|
||||
* [DigitalOcean](https://www.edgedb.com/docs/guides/deployment/digitalocean)
|
||||
*
|
||||
* [Fly.io](https://www.edgedb.com/docs/guides/deployment/fly_io)
|
||||
*
|
||||
* [Docker](https://www.edgedb.com/docs/guides/deployment/docker) (cloud-agnostic)
|
||||
*
|
||||
* ### Find your instance’s DSN
|
||||
*
|
||||
* The DSN is also known as a connection string. It will have the format `edgedb://username:password@hostname:port`. The exact instructions for this depend on which cloud you are deploying to.
|
||||
*
|
||||
* ### Set an environment variable
|
||||
*
|
||||
* ```env title=".env"
|
||||
* EDGEDB_DSN=edgedb://johndoe:supersecure@myhost.com:420
|
||||
* ```
|
||||
*
|
||||
* ### Update the client
|
||||
*
|
||||
* ```diff title="pages/api/auth/[...nextauth].js"
|
||||
* import NextAuth from "next-auth"
|
||||
* import GoogleProvider from "next-auth/providers/google"
|
||||
* import { EdgeDBAdapter } from "@auth/edgedb-adapter"
|
||||
* import { createClient } from "edgedb"
|
||||
*
|
||||
* - const client = createClient()
|
||||
* + const client = createClient({ dsn: process.env.EDGEDB_DSN })
|
||||
*
|
||||
* export default NextAuth({
|
||||
* adapter: EdgeDBAdapter(client),
|
||||
* providers: [
|
||||
* GoogleProvider({
|
||||
* clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*
|
||||
* ### Apply migrations
|
||||
*
|
||||
* Use the DSN to apply migrations against your remote instance.
|
||||
*
|
||||
* ```bash
|
||||
* edgedb migrate --dsn <your-instance-dsn>
|
||||
* ```
|
||||
*
|
||||
* ### Set up a `prebuild` script
|
||||
*
|
||||
* Add the following `prebuild` script to your `package.json`. When your hosting provider initializes the build, it will trigger this script which will generate the query builder. The `npx @edgedb/generate edgeql-js` command will read the value of the `EDGEDB_DSN` environment variable, connect to the database, and generate the query builder before your hosting provider starts building the project.
|
||||
*
|
||||
* ```diff title="package.json"
|
||||
* "scripts": {
|
||||
* "dev": "next dev",
|
||||
* "build": "next build",
|
||||
* "start": "next start",
|
||||
* "lint": "next lint",
|
||||
* + "prebuild": "npx @edgedb/generate edgeql-js"
|
||||
* },
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export function EdgeDBAdapter(client: Client): Adapter {
|
||||
return {
|
||||
async createUser({ email, emailVerified, name, image }) {
|
||||
return await client.queryRequiredSingle(
|
||||
`
|
||||
with
|
||||
image := <optional str>$image,
|
||||
name := <optional str>$name,
|
||||
emailVerified := <optional str>$emailVerified
|
||||
|
||||
select (
|
||||
insert User {
|
||||
email:= <str>$email,
|
||||
emailVerified:= <datetime>emailVerified,
|
||||
name:= name,
|
||||
image:= image,
|
||||
}
|
||||
) {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
name,
|
||||
image
|
||||
}
|
||||
`,
|
||||
{
|
||||
email,
|
||||
emailVerified: emailVerified && new Date(emailVerified).toISOString(),
|
||||
name,
|
||||
image,
|
||||
}
|
||||
)
|
||||
},
|
||||
async getUser(id) {
|
||||
return await client.querySingle<AdapterUser>(
|
||||
`
|
||||
select User {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
name,
|
||||
image
|
||||
} filter .id = <uuid>$id;
|
||||
`,
|
||||
{ id }
|
||||
)
|
||||
},
|
||||
async getUserByEmail(email) {
|
||||
return await client.querySingle<AdapterUser>(
|
||||
`
|
||||
select User {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
name,
|
||||
image
|
||||
} filter .email = <str>$email;
|
||||
`,
|
||||
{ email }
|
||||
)
|
||||
},
|
||||
async getUserByAccount({ providerAccountId, provider }) {
|
||||
return await client.querySingle<AdapterUser>(
|
||||
`
|
||||
with account := (
|
||||
select Account
|
||||
filter .providerAccountId = <str>$providerAccountId
|
||||
and .provider = <str>$provider
|
||||
)
|
||||
select account.user {
|
||||
id,
|
||||
email,
|
||||
image,
|
||||
name,
|
||||
emailVerified
|
||||
}
|
||||
`,
|
||||
{ providerAccountId, provider }
|
||||
)
|
||||
},
|
||||
async updateUser({ email, emailVerified, id, image, name }) {
|
||||
return await client.queryRequiredSingle<AdapterUser>(
|
||||
`
|
||||
with
|
||||
email := <optional str>$email,
|
||||
emailVerified := <optional str>$emailVerified,
|
||||
image := <optional str>$image,
|
||||
name := <optional str>$name
|
||||
|
||||
select (
|
||||
update User
|
||||
filter .id = <uuid>$id
|
||||
set {
|
||||
email := email ?? .email,
|
||||
emailVerified := <datetime>emailVerified ?? .emailVerified,
|
||||
image := image ?? .image,
|
||||
name := name ?? .name,
|
||||
}
|
||||
) {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
name
|
||||
}
|
||||
`,
|
||||
{
|
||||
email,
|
||||
emailVerified: emailVerified && new Date(emailVerified).toISOString(),
|
||||
id,
|
||||
image,
|
||||
name,
|
||||
}
|
||||
)
|
||||
},
|
||||
async deleteUser(id) {
|
||||
await client.execute(`delete User filter .id = <uuid>$id;`, { id })
|
||||
},
|
||||
async linkAccount({
|
||||
userId,
|
||||
type,
|
||||
provider,
|
||||
providerAccountId,
|
||||
refresh_token,
|
||||
access_token,
|
||||
expires_at,
|
||||
token_type,
|
||||
scope,
|
||||
id_token,
|
||||
session_state,
|
||||
}) {
|
||||
await client.execute(
|
||||
`
|
||||
with
|
||||
userId := <optional str>$userId,
|
||||
refresh_token := <optional str>$refresh_token,
|
||||
access_token := <optional str>$access_token,
|
||||
expires_at := <optional str>$expires_at,
|
||||
token_type := <optional str>$token_type,
|
||||
scope := <optional str>$scope,
|
||||
id_token := <optional str>$id_token,
|
||||
session_state := <optional str>$session_state
|
||||
|
||||
insert Account {
|
||||
type := <str>$type,
|
||||
provider := <str>$provider,
|
||||
providerAccountId := <str>$providerAccountId,
|
||||
refresh_token := refresh_token,
|
||||
access_token := access_token,
|
||||
expires_at := <int64>expires_at,
|
||||
token_type := token_type,
|
||||
scope := scope,
|
||||
id_token := id_token,
|
||||
session_state := session_state,
|
||||
user := (
|
||||
select User filter .id = <uuid>userId
|
||||
)
|
||||
}
|
||||
`,
|
||||
{
|
||||
userId,
|
||||
type,
|
||||
provider,
|
||||
providerAccountId,
|
||||
refresh_token,
|
||||
access_token,
|
||||
expires_at: expires_at && String(expires_at),
|
||||
token_type,
|
||||
scope,
|
||||
id_token,
|
||||
session_state,
|
||||
}
|
||||
)
|
||||
},
|
||||
async unlinkAccount({ providerAccountId, provider }) {
|
||||
await client.execute(
|
||||
`
|
||||
delete Account filter
|
||||
.providerAccountId = <str>$providerAccountId
|
||||
and
|
||||
.provider = <str>$provider
|
||||
`,
|
||||
{ providerAccountId, provider }
|
||||
)
|
||||
},
|
||||
async createSession({ expires, sessionToken, userId }) {
|
||||
return await client.queryRequiredSingle<AdapterSession>(
|
||||
`
|
||||
select (
|
||||
insert Session {
|
||||
expires := <datetime>$expires,
|
||||
sessionToken := <str>$sessionToken,
|
||||
user := (
|
||||
select User filter .id = <uuid>$userId
|
||||
)
|
||||
}
|
||||
) {
|
||||
expires,
|
||||
sessionToken,
|
||||
userId
|
||||
};
|
||||
`,
|
||||
{ expires, sessionToken, userId }
|
||||
)
|
||||
},
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const sessionAndUser = await client.querySingle<
|
||||
AdapterSession & { user: AdapterUser }
|
||||
>(
|
||||
`
|
||||
select Session {
|
||||
userId,
|
||||
id,
|
||||
expires,
|
||||
sessionToken,
|
||||
user: {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
name
|
||||
}
|
||||
} filter .sessionToken = <str>$sessionToken;
|
||||
`,
|
||||
{ sessionToken }
|
||||
)
|
||||
|
||||
if (!sessionAndUser) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { user, ...session } = sessionAndUser
|
||||
|
||||
if (!user || !session) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
session,
|
||||
}
|
||||
},
|
||||
async updateSession({ sessionToken, expires, userId }) {
|
||||
return await client.querySingle<AdapterSession>(
|
||||
`
|
||||
with
|
||||
sessionToken := <optional str>$sessionToken,
|
||||
expires := <optional str>$expires,
|
||||
userId := <optional str>$userId,
|
||||
user := (
|
||||
select User filter .id = <uuid>userId
|
||||
)
|
||||
|
||||
select (
|
||||
update Session
|
||||
filter .sessionToken = <str>$sessionToken
|
||||
set {
|
||||
sessionToken := sessionToken ?? .sessionToken,
|
||||
expires := <datetime>expires ?? .expires,
|
||||
user := user ?? .user
|
||||
}
|
||||
) {
|
||||
sessionToken,
|
||||
userId,
|
||||
expires
|
||||
}
|
||||
`,
|
||||
{
|
||||
sessionToken,
|
||||
expires: expires && new Date(expires).toISOString(),
|
||||
userId,
|
||||
}
|
||||
)
|
||||
},
|
||||
async deleteSession(sessionToken) {
|
||||
await client.query(
|
||||
`delete Session filter .sessionToken = <str>$sessionToken`,
|
||||
{ sessionToken }
|
||||
)
|
||||
},
|
||||
async createVerificationToken({ identifier, expires, token }) {
|
||||
const createdVerificationToken =
|
||||
await client.querySingle<VerificationToken>(
|
||||
`
|
||||
select (
|
||||
insert VerificationToken {
|
||||
identifier := <str>$identifier,
|
||||
expires := <datetime>$expires,
|
||||
token := <str>$token,
|
||||
}
|
||||
) {
|
||||
identifier,
|
||||
expires,
|
||||
token
|
||||
}
|
||||
`,
|
||||
{ identifier, expires, token }
|
||||
)
|
||||
|
||||
return createdVerificationToken
|
||||
},
|
||||
async useVerificationToken({ token, identifier }) {
|
||||
const verificationToken = await client.querySingle<VerificationToken>(
|
||||
`
|
||||
select (
|
||||
delete VerificationToken filter .token = <str>$token
|
||||
and
|
||||
.identifier = <str>$identifier
|
||||
) {
|
||||
identifier,
|
||||
expires,
|
||||
token
|
||||
}
|
||||
`,
|
||||
{ token, identifier }
|
||||
)
|
||||
|
||||
if (verificationToken && "id" in verificationToken) {
|
||||
delete verificationToken.id
|
||||
}
|
||||
return verificationToken
|
||||
},
|
||||
}
|
||||
}
|
||||
91
packages/adapter-edgedb/tests/index.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { runBasicTests } from "@auth/adapter-test"
|
||||
import { EdgeDBAdapter } from "../src"
|
||||
import { createClient } from "edgedb"
|
||||
|
||||
if (process.env.CI) {
|
||||
// TODO: Fix this
|
||||
test('Skipping EdgeDBAdapter tests in CI because of "Request failed" errors. Should revisit', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const client = createClient();
|
||||
|
||||
runBasicTests({
|
||||
adapter: EdgeDBAdapter(client),
|
||||
db: {
|
||||
connect: async () => {
|
||||
await client.query(`
|
||||
delete User;
|
||||
delete Account;
|
||||
delete Session;
|
||||
delete VerificationToken;
|
||||
`)
|
||||
},
|
||||
disconnect: async () => {
|
||||
await client.query(`
|
||||
delete User;
|
||||
delete Account;
|
||||
delete Session;
|
||||
delete VerificationToken;
|
||||
`)
|
||||
},
|
||||
user: async (id) => {
|
||||
return await client.querySingle(`
|
||||
select User {
|
||||
id,
|
||||
email,
|
||||
emailVerified,
|
||||
name,
|
||||
image
|
||||
} filter .id = <uuid>$id
|
||||
`, { id })
|
||||
},
|
||||
account: async ({ providerAccountId, provider }) => {
|
||||
return await client.querySingle(`
|
||||
select Account {
|
||||
provider,
|
||||
providerAccountId,
|
||||
type,
|
||||
access_token,
|
||||
expires_at,
|
||||
id_token,
|
||||
refresh_token,
|
||||
token_type,
|
||||
scope,
|
||||
session_state,
|
||||
id,
|
||||
userId
|
||||
}
|
||||
filter
|
||||
.providerAccountId = <str>$providerAccountId
|
||||
and
|
||||
.provider = <str>$provider
|
||||
`, { providerAccountId, provider })
|
||||
},
|
||||
session: async (sessionToken) => {
|
||||
return await client.querySingle(`
|
||||
select Session {
|
||||
userId,
|
||||
id,
|
||||
expires,
|
||||
sessionToken,
|
||||
}
|
||||
filter .sessionToken = <str>$sessionToken
|
||||
`, { sessionToken })
|
||||
},
|
||||
async verificationToken({ token, identifier }) {
|
||||
return await client.querySingle(`
|
||||
select VerificationToken {
|
||||
identifier,
|
||||
expires,
|
||||
token,
|
||||
}
|
||||
filter .token = <str>$token
|
||||
and
|
||||
.identifier = <str>$identifier
|
||||
`, { token, identifier })
|
||||
},
|
||||
},
|
||||
})
|
||||
20
packages/adapter-edgedb/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@auth/tsconfig/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"isolatedModules": true,
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"outDir": ".",
|
||||
"rootDir": "src",
|
||||
"skipDefaultLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"stripInternal": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["*.js", "*.d.ts"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/mongodb-adapter",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"description": "MongoDB adapter for Auth.js",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -42,15 +42,15 @@
|
||||
"@auth/core": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mongodb": "^5 || ^4"
|
||||
"mongodb": "^6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@auth/adapter-test": "workspace:*",
|
||||
"@auth/tsconfig": "workspace:*",
|
||||
"jest": "^27.4.3",
|
||||
"mongodb": "^5.1.0"
|
||||
"mongodb": "^6.0.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export function MongoDBAdapter(
|
||||
await db
|
||||
).U.findOneAndUpdate({ _id }, { $set: user }, { returnDocument: "after" })
|
||||
|
||||
return from<AdapterUser>(result.value!)
|
||||
return from<AdapterUser>(result!)
|
||||
},
|
||||
async deleteUser(id) {
|
||||
const userId = _id(id)
|
||||
@@ -210,7 +210,7 @@ export function MongoDBAdapter(
|
||||
return account
|
||||
},
|
||||
async unlinkAccount(provider_providerAccountId) {
|
||||
const { value: account } = await (
|
||||
const account = await (
|
||||
await db
|
||||
).A.findOneAndDelete(provider_providerAccountId)
|
||||
return from<AdapterAccount>(account!)
|
||||
@@ -235,17 +235,17 @@ export function MongoDBAdapter(
|
||||
async updateSession(data) {
|
||||
const { _id, ...session } = to<AdapterSession>(data)
|
||||
|
||||
const result = await (
|
||||
const updatedSession = await (
|
||||
await db
|
||||
).S.findOneAndUpdate(
|
||||
{ sessionToken: session.sessionToken },
|
||||
{ $set: session },
|
||||
{ returnDocument: "after" }
|
||||
)
|
||||
return from<AdapterSession>(result.value!)
|
||||
return from<AdapterSession>(updatedSession!)
|
||||
},
|
||||
async deleteSession(sessionToken) {
|
||||
const { value: session } = await (
|
||||
const session = await (
|
||||
await db
|
||||
).S.findOneAndDelete({
|
||||
sessionToken,
|
||||
@@ -257,14 +257,13 @@ export function MongoDBAdapter(
|
||||
return data
|
||||
},
|
||||
async useVerificationToken(identifier_token) {
|
||||
const { value: verificationToken } = await (
|
||||
const verificationToken = await (
|
||||
await db
|
||||
).V.findOneAndDelete(identifier_token)
|
||||
|
||||
if (!verificationToken) return null
|
||||
// @ts-expect-error
|
||||
delete verificationToken._id
|
||||
return verificationToken
|
||||
const { _id, ...rest } = verificationToken;
|
||||
return rest;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"@auth/tsconfig": "workspace:*",
|
||||
"@prisma/client": "^5.2.0",
|
||||
"jest": "^27.4.3",
|
||||
"mongodb": "^4.4.0",
|
||||
"mongodb": "^4.17.0",
|
||||
"prisma": "^5.2.0"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/sequelize-adapter",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Sequelize adapter for Auth.js",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -51,4 +51,4 @@
|
||||
"jest": {
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,7 +301,9 @@ export default function SequelizeAdapter(
|
||||
async deleteSession(sessionToken) {
|
||||
await sync()
|
||||
|
||||
const session = await Session.findOne({ where: { sessionToken } })
|
||||
await Session.destroy({ where: { sessionToken } })
|
||||
return session?.get({ plain: true })
|
||||
},
|
||||
async createVerificationToken(token) {
|
||||
await sync()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/core",
|
||||
"version": "0.13.0",
|
||||
"version": "0.15.0",
|
||||
"description": "Authentication for the Web.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
|
||||
103
packages/core/src/providers/click-up.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#24292f", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>ClickUp</b> integration.</span>
|
||||
* <a href="https://clickup.com">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/click-up.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* @module providers/ClickUp
|
||||
*/
|
||||
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
/** @see [Get the authenticated user](https://clickup.com/api/clickupreference/operation/GetAuthorizedUser/)*/
|
||||
export interface ClickUpProfile {
|
||||
user: {
|
||||
id: number
|
||||
username: string
|
||||
color: string
|
||||
profilePicture: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ClickUp login to your page and make requests to [ClickUp APIs](https://clickup.com/api/).
|
||||
*
|
||||
* ### Setup
|
||||
*
|
||||
* #### Callback URL
|
||||
* ```
|
||||
* https://example.com/api/auth/callback/clickup
|
||||
* ```
|
||||
*
|
||||
* #### Configuration
|
||||
* ```ts
|
||||
* import { Auth } from "@auth/core"
|
||||
* import ClickUp from "@auth/core/providers/click-up"
|
||||
*
|
||||
* const request = new Request(origin)
|
||||
* const response = await Auth(request, {
|
||||
* providers: [ClickUp({ clientId: CLICKUP_CLIENT_ID, clientSecret: CLICKUP_CLIENT_SECRET })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ### Resources
|
||||
*
|
||||
* - [ClickUp - Authorizing OAuth Apps](https://clickup.com/api/developer-portal/authentication#oauth-flow)
|
||||
* - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts)
|
||||
*
|
||||
* ### Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the ClickUp provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The ClickUp provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts).
|
||||
* 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).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
export default function ClickUp(
|
||||
config: OAuthUserConfig<ClickUpProfile>
|
||||
): OAuthConfig<ClickUpProfile> {
|
||||
return {
|
||||
id: "clickup",
|
||||
name: "ClickUp",
|
||||
type: "oauth",
|
||||
authorization: "https://app.clickup.com/api",
|
||||
token: "https://api.clickup.com/api/v2/oauth/token",
|
||||
userinfo: "https://api.clickup.com/api/v2/user",
|
||||
clientId: config.clientId,
|
||||
clientSecret: config.clientSecret,
|
||||
checks: ["state"],
|
||||
profile: (profile: ClickUpProfile) => {
|
||||
return {
|
||||
id: profile.user.id.toString(),
|
||||
name: profile.user.username,
|
||||
profilePicture: profile.user.profilePicture,
|
||||
color: profile.user.color,
|
||||
}
|
||||
},
|
||||
style: {
|
||||
logo: "/click-up.svg",
|
||||
logoDark: "/click-up.svg",
|
||||
bg: "#fff",
|
||||
bgDark: "#24292f",
|
||||
text: "#000",
|
||||
textDark: "#fff",
|
||||
},
|
||||
options: config,
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,19 @@ export type CredentialsProviderType = "Credentials"
|
||||
* with supporting usernames and passwords.
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token. For example, you can add additional information to the token by returning an object from the `jwt()` callback:
|
||||
*
|
||||
* ```js
|
||||
* callbacks: {
|
||||
* async jwt(token, user, account, profile, isNewUser) {
|
||||
* if (user) {
|
||||
* token.id = user.id
|
||||
* }
|
||||
* return token
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
|
||||
123
packages/core/src/providers/dribbble.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Dribbble</b> integration.</span>
|
||||
* <a href="https://dribbble.com">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/dribbble.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* @module providers/dribbble
|
||||
*/
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
export interface DribbbleProfile extends Record<string, any> {
|
||||
id: number
|
||||
name: string
|
||||
email: string
|
||||
avatar_url: string
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add Dribbble login to your page.
|
||||
*
|
||||
* ### Setup
|
||||
*
|
||||
* #### Callback URL
|
||||
* ```
|
||||
* https://example.com/api/auth/callback/dribbble
|
||||
* ```
|
||||
*
|
||||
* #### Configuration
|
||||
*```js
|
||||
* import Auth from "@auth/core"
|
||||
* import Google from "@auth/core/providers/dribbble"
|
||||
*
|
||||
* const request = new Request(origin)
|
||||
* const response = await Auth(request, {
|
||||
* providers: [Dribbble({ clientId: DRIBBBLE_CLIENT_ID, clientSecret: DRIBBBLE_CLIENT_SECRET })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ### Resources
|
||||
*
|
||||
* - [Dribbble API](https://developer.dribbble.com)
|
||||
* - [Dribbble OAuth](https://developer.dribbble.com/v2/oauth/)
|
||||
* - [Dribbble Applications](https://dribbble.com/account/applications/new)
|
||||
*
|
||||
* ### Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the GitHub provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Dribbble provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/dribbble.ts).
|
||||
* 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).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::tip
|
||||
* You can optionally set the scope to `public upload` for more advanced scenarios. If omitted, the default `public` scope will be used for authentication purposes.
|
||||
* :::
|
||||
*/
|
||||
|
||||
export default function Dribbble<P extends DribbbleProfile>(
|
||||
options: OAuthUserConfig<P> & {
|
||||
/**
|
||||
* Reference: https://developer.dribbble.com/v2/oauth/#scopes
|
||||
*
|
||||
* For the purposes of NextAuth.js `upload`-only scope makes no sense,
|
||||
* therefore it is excluded from suggested values. Treated by Dribbble as `public` when omitted.
|
||||
*
|
||||
* @default public
|
||||
*/
|
||||
scope?: "public" | "public upload"
|
||||
}
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "dribbble",
|
||||
name: "Dribbble",
|
||||
type: "oauth",
|
||||
|
||||
authorization: {
|
||||
url: "https://dribbble.com/oauth/authorize",
|
||||
params: { scope: options.scope },
|
||||
},
|
||||
|
||||
token: "https://dribbble.com/oauth/token",
|
||||
userinfo: "https://api.dribbble.com/v2/user",
|
||||
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id.toString(),
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
},
|
||||
|
||||
style: {
|
||||
// Light mode
|
||||
logo: "/dribbble.svg",
|
||||
text: "#ea4c89",
|
||||
bg: "#fff",
|
||||
// Dark mode
|
||||
logoDark: "/dribbble-dark.svg",
|
||||
textDark: "#fff",
|
||||
bgDark: "#000",
|
||||
},
|
||||
|
||||
options,
|
||||
}
|
||||
}
|
||||
106
packages/core/src/providers/mastodon.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Mastodon</b> integration.</span>
|
||||
* <a href="https://mastodon.social">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/mastodon.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* @module providers/mastodon
|
||||
*/
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
export interface MastodonProfile extends Record<string, any> {
|
||||
id: string
|
||||
username: string
|
||||
acct: string
|
||||
display_name: string
|
||||
locked: boolean
|
||||
bot: boolean
|
||||
created_at: string
|
||||
note: string
|
||||
url: string
|
||||
avatar: string
|
||||
avatar_static: string
|
||||
header: string
|
||||
header_static: string
|
||||
followers_count: number
|
||||
following_count: number
|
||||
statuses_count: number
|
||||
last_status_at: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Mastodon login to your page.
|
||||
*
|
||||
* ### Setup
|
||||
*
|
||||
* #### Callback URL
|
||||
* ```
|
||||
* https://example.com/api/auth/callback/mastodon
|
||||
* ```
|
||||
*
|
||||
* #### Configuration
|
||||
*```js
|
||||
* import Auth from "@auth/core"
|
||||
* import Mastodon from "@auth/core/providers/mastodon"
|
||||
*
|
||||
* const request = new Request(origin)
|
||||
* const response = await Auth(request, {
|
||||
* providers: [Mastodon({ clientId: MASTODON_CLIENT_ID, clientSecret: MASTODON_CLIENT_SECRET, issuer: MASTODON_ISSUER })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ### Resources
|
||||
*
|
||||
* - [Mastodon OAuth documentation](https://docs.joinmastodon.org/client/token/)
|
||||
* - [Mastodon OAuth Configuration](https://mastodon.social/settings/applications)
|
||||
*
|
||||
* ### Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the Mastodon provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* Due to Mastodons infrastructure beeing a Fediverse you have to define the `issuer` you want to connect to.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Mastodon provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/mastodon.ts).
|
||||
* 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).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
|
||||
export default function Mastodon<P extends MastodonProfile>(
|
||||
options: OAuthUserConfig<P> & {
|
||||
issuer: string
|
||||
}
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "mastodon",
|
||||
name: "Mastodon",
|
||||
type: "oauth",
|
||||
authorization: `${options.issuer}/oauth/authorize?scope=read`,
|
||||
token: `${options.issuer}/oauth/token`,
|
||||
userinfo: `${options.issuer}/api/v1/accounts/verify_credentials`,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.username,
|
||||
image: profile.avatar_static,
|
||||
email: null,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
269
packages/core/src/providers/tiktok.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Tiktok</b> integration.</span>
|
||||
* <a href="https://www.tiktok.com/">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/tiktok.svg" height="48" />
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* @module providers/tiktok
|
||||
*/
|
||||
import { TokenSet } from "src/types.js"
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
/**
|
||||
* [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
|
||||
*/
|
||||
export interface TiktokProfile extends Record<string, any> {
|
||||
data: {
|
||||
user: {
|
||||
/**
|
||||
* The unique identification of the user in the current application.Open id
|
||||
* for the client.
|
||||
*
|
||||
* To return this field, add `fields=open_id` in the user profile request's query parameter.
|
||||
*/
|
||||
open_id: string
|
||||
/**
|
||||
* The unique identification of the user across different apps for the same developer.
|
||||
* For example, if a partner has X number of clients,
|
||||
* it will get X number of open_id for the same TikTok user,
|
||||
* but one persistent union_id for the particular user.
|
||||
*
|
||||
* To return this field, add `fields=union_id` in the user profile request's query parameter.
|
||||
*/
|
||||
union_id?: string
|
||||
/**
|
||||
* User's profile image.
|
||||
*
|
||||
* To return this field, add `fields=avatar_url` in the user profile request's query parameter.
|
||||
*/
|
||||
avatar_url: string
|
||||
/**
|
||||
* User`s profile image in 100x100 size.
|
||||
*
|
||||
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
|
||||
*/
|
||||
avatar_url_100?: string
|
||||
/**
|
||||
* User's profile image with higher resolution
|
||||
*
|
||||
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
|
||||
*/
|
||||
avatar_large_url?: string
|
||||
/**
|
||||
* User's profile name
|
||||
*
|
||||
* To return this field, add `fields=display_name` in the user profile request's query parameter.
|
||||
*/
|
||||
display_name: string
|
||||
/**
|
||||
* User's username.
|
||||
*
|
||||
* To return this field, add `fields=username` in the user profile request's query parameter.
|
||||
*/
|
||||
username: string
|
||||
/** @note Email is currently unsupported by Tiktok */
|
||||
email?: string
|
||||
/**
|
||||
* User's bio description if there is a valid one.
|
||||
*
|
||||
* To return this field, add `fields=bio_description` in the user profile request's query parameter.
|
||||
*/
|
||||
bio_description?: string
|
||||
/**
|
||||
* The link to user's TikTok profile page.
|
||||
*
|
||||
* To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.
|
||||
*/
|
||||
profile_deep_link?: string
|
||||
/**
|
||||
* Whether TikTok has provided a verified badge to the account after confirming
|
||||
* that it belongs to the user it represents.
|
||||
*
|
||||
* To return this field, add `fields=is_verified` in the user profile request's query parameter.
|
||||
*/
|
||||
is_verified?: boolean
|
||||
/**
|
||||
* User's followers count.
|
||||
*
|
||||
* To return this field, add `fields=follower_count` in the user profile request's query parameter.
|
||||
*/
|
||||
follower_count?: number
|
||||
/**
|
||||
* The number of accounts that the user is following.
|
||||
*
|
||||
* To return this field, add `fields=following_count` in the user profile request's query parameter.
|
||||
*/
|
||||
following_count?: number
|
||||
/**
|
||||
* The total number of likes received by the user across all of their videos.
|
||||
*
|
||||
* To return this field, add `fields=likes_count` in the user profile request's query parameter.
|
||||
*/
|
||||
likes_count?: number
|
||||
/**
|
||||
* The total number of publicly posted videos by the user.
|
||||
*
|
||||
* To return this field, add `fields=video_count` in the user profile request's query parameter.
|
||||
*/
|
||||
video_count?: number
|
||||
}
|
||||
}
|
||||
error: {
|
||||
/**
|
||||
* The error category in string.
|
||||
*/
|
||||
code: string
|
||||
/**
|
||||
* The error message in string.
|
||||
*/
|
||||
message: string
|
||||
/**
|
||||
* The error message in string.
|
||||
*/
|
||||
log_id: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Tiktok login to your page.
|
||||
*
|
||||
* ### Setup
|
||||
*
|
||||
* #### Callback URL
|
||||
* ```
|
||||
* https://example.com/api/auth/callback/tiktok
|
||||
* ```
|
||||
*
|
||||
* #### Configuration
|
||||
*```js
|
||||
* import Auth from "@auth/core"
|
||||
* import Tiktok from "@auth/core/providers/tiktok"
|
||||
*
|
||||
* const request = new Request(origin)
|
||||
* const response = await Auth(request, {
|
||||
* providers: [Tiktok({ clientId: TIKTOK_CLIENT_KEY, clientSecret: TIKTOK_CLIENT_SECRET })],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ### Resources
|
||||
* - [Tiktok app console](https://developers.tiktok.com/)
|
||||
* - [Tiktok login kit documentation](https://developers.tiktok.com/doc/login-kit-web/)
|
||||
* - [Avaliable Scopes](https://developers.tiktok.com/doc/tiktok-api-scopes/)
|
||||
*
|
||||
*
|
||||
* ### Notes
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* Production applications cannot use localhost URLs to sign in with Tiktok. You need add the domain and Callback/Redirect url's to your Tiktok app and have them review and approved by the Tiktok Team.
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* Email address is not supported by Tiktok.
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* Client_ID will be the Client Key in the Tiktok Application
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* By default, Auth.js assumes that the Tiktok provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Tiktok provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/tiktok.ts).
|
||||
* 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).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
export default function Tiktok<P extends TiktokProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "tiktok",
|
||||
name: "TikTok",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://www.tiktok.com/v2/auth/authorize",
|
||||
params: {
|
||||
client_key: options.clientId,
|
||||
scope: "user.info.profile",
|
||||
response_type: "code",
|
||||
},
|
||||
},
|
||||
|
||||
token: {
|
||||
async request({ params, provider }) {
|
||||
const res = await fetch(`https://open.tiktokapis.com/v2/oauth/token/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_key: provider.clientId!,
|
||||
client_secret: provider.clientSecret!,
|
||||
code: params.code!,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: provider.callbackUrl!,
|
||||
}),
|
||||
}).then((res) => res.json())
|
||||
|
||||
const tokens: TokenSet = {
|
||||
access_token: res.access_token,
|
||||
expires_at: res.expires_in,
|
||||
refresh_token: res.refresh_token,
|
||||
scope: res.scope,
|
||||
id_token: res.open_id,
|
||||
token_type: res.token_type,
|
||||
session_state: res.open_id,
|
||||
}
|
||||
return {
|
||||
tokens,
|
||||
}
|
||||
},
|
||||
},
|
||||
userinfo: {
|
||||
url: "https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,username",
|
||||
async request({ tokens, provider }) {
|
||||
return await fetch(provider.userinfo?.url as URL, {
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||||
}).then(async (res) => await res.json())
|
||||
},
|
||||
},
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.data.user.open_id,
|
||||
name: profile.data.user.display_name,
|
||||
image: profile.data.user.avatar_url,
|
||||
email: profile.data.user.email || null,
|
||||
}
|
||||
},
|
||||
style: {
|
||||
logo: "/tiktok.svg",
|
||||
logoDark: "/tiktok-dark.svg",
|
||||
bg: "#fff",
|
||||
bgDark: "#000",
|
||||
text: "#000",
|
||||
textDark: "#fff",
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/sveltekit",
|
||||
"version": "0.3.6",
|
||||
"version": "0.3.7",
|
||||
"description": "Authentication for SvelteKit.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
@@ -69,4 +69,4 @@
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
BuiltInProviderType,
|
||||
RedirectableProviderType,
|
||||
} from "@auth/core/providers"
|
||||
import { base } from "$app/paths";
|
||||
|
||||
/**
|
||||
* Client-side method to initiate a signin flow
|
||||
@@ -34,16 +35,15 @@ export async function signIn<
|
||||
const isEmail = providerId === "email"
|
||||
const isSupportingReturn = isCredentials || isEmail
|
||||
|
||||
// TODO: Handle custom base path
|
||||
const signInUrl = `/auth/${
|
||||
const basePath = base ?? ""
|
||||
const signInUrl = `${basePath}/auth/${
|
||||
isCredentials ? "callback" : "signin"
|
||||
}/${providerId}`
|
||||
|
||||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
|
||||
|
||||
// TODO: Handle custom base path
|
||||
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
|
||||
const csrfTokenResponse = await fetch("/auth/csrf")
|
||||
const csrfTokenResponse = await fetch(`${basePath}/auth/csrf`)
|
||||
const { csrfToken } = await csrfTokenResponse.json()
|
||||
|
||||
const res = await fetch(_signInUrl, {
|
||||
@@ -81,11 +81,11 @@ export async function signIn<
|
||||
*/
|
||||
export async function signOut(options?: SignOutParams) {
|
||||
const { callbackUrl = window.location.href } = options ?? {}
|
||||
// TODO: Custom base path
|
||||
const basePath = base ?? ""
|
||||
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
|
||||
const csrfTokenResponse = await fetch("/auth/csrf")
|
||||
const csrfTokenResponse = await fetch(`${basePath}/auth/csrf`)
|
||||
const { csrfToken } = await csrfTokenResponse.json()
|
||||
const res = await fetch(`/auth/signout`, {
|
||||
const res = await fetch(`${basePath}/auth/signout`, {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
|
||||
@@ -204,6 +204,7 @@
|
||||
import type { Handle, RequestEvent } from "@sveltejs/kit"
|
||||
|
||||
import { dev } from "$app/environment"
|
||||
import { base } from "$app/paths"
|
||||
import { env } from "$env/dynamic/private"
|
||||
|
||||
import { Auth } from "@auth/core"
|
||||
@@ -216,7 +217,7 @@ export async function getSession(
|
||||
config.secret ??= env.AUTH_SECRET
|
||||
config.trustHost ??= true
|
||||
|
||||
const prefix = config.prefix ?? "/auth"
|
||||
const prefix = config.prefix ?? `${base}/auth`
|
||||
const url = new URL(prefix + "/session", req.url)
|
||||
const request = new Request(url, { headers: req.headers })
|
||||
const response = await Auth(request, config)
|
||||
@@ -236,7 +237,7 @@ export interface SvelteKitAuthConfig extends AuthConfig {
|
||||
* If you change the default value,
|
||||
* you must also update the callback URL used by the [providers](https://authjs.dev/reference/core/providers).
|
||||
*
|
||||
* @default "/auth"
|
||||
* @default `${base}/auth` - `base` is the base path of your SvelteKit app, configured in `svelte.config.js`.
|
||||
*/
|
||||
prefix?: string
|
||||
}
|
||||
@@ -260,7 +261,7 @@ function AuthHandle(svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKit
|
||||
typeof svelteKitAuthOptions === "object"
|
||||
? svelteKitAuthOptions
|
||||
: await svelteKitAuthOptions(event)
|
||||
const { prefix = "/auth" } = authOptions
|
||||
const { prefix = `${base}/auth` } = authOptions
|
||||
const { url, request } = event
|
||||
|
||||
event.locals.getSession ??= () => getSession(request, authOptions)
|
||||
@@ -285,6 +286,7 @@ export function SvelteKitAuth(options: SvelteKitAuthConfig | DynamicSvelteKitAut
|
||||
if (typeof options === "object") {
|
||||
options.secret ??= env.AUTH_SECRET
|
||||
options.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
|
||||
options.prefix ??= `${base}/auth`
|
||||
}
|
||||
return AuthHandle(options)
|
||||
}
|
||||
|
||||