Compare commits

..

8 Commits

Author SHA1 Message Date
Thang Vu
bfcf1a3604 chore(release): bump version [skip ci] 2023-01-31 19:25:50 +07:00
Thang Vu
5b1555ed97 feat: redesign all default pages
sync from core via #5825

Co-Authored-By: Rein Undheim <46612252+Gawdfrey@users.noreply.github.com>
2023-01-31 17:40:46 +07:00
Vu Van Dung
0ed07b31b6 fix(ts): correctly type unstable_getServerSession (#6560)
* fix: type of unstable_getServerSession

Signed-off-by: Vu Van Dung <me@joulev.dev>

* Apply suggestions from code review

---------

Signed-off-by: Vu Van Dung <me@joulev.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-01-30 12:10:26 +00:00
OrJDev
2311be7589 docs: Remove the unstable note. (#6537) 2023-01-28 15:15:09 +01:00
Balázs Orbán
e847b3466f chore(release): bump version [skip ci] 2023-01-28 13:24:50 +01:00
Balázs Orbán
8df6d5b469 feat: make generateSessionToken awaitable (#6536)
Co-authored-by: @HommeSauvage
2023-01-28 12:19:32 +00:00
Balázs Orbán
0bcaeca369 feat: remove unstable_ prefix getServerSession (#6535)
* feat: remove `unstable_` prefix from `getServerSession`

* fix test

* fix lint
2023-01-28 12:12:00 +00:00
Balázs Orbán
4f5ddbcb76 fix(oauth1): pass oauth_token_secret (#6534)
* Pass oauth_token_secret in OAuth 1.0 calls

* simplify

* simplify

---------

Co-authored-by: dawidos234 <dawidos234@gmail.com>
2023-01-28 09:44:23 +00:00
13 changed files with 99 additions and 63 deletions

View File

@@ -163,10 +163,6 @@ NextAuth.js provides a `getSession()` helper which should be called **client sid
On the server side, **this is still available to use**, however, we recommend using `getServerSession` going forward. The idea behind this is to avoid an additional unnecessary `fetch` call on the server side. For more information, please check out [this issue](https://github.com/nextauthjs/next-auth/issues/1535).
:::note
The `getServerSession` only has the prefix `unstable_` at the moment, because the API may change in the future. There are no known bugs at the moment and it is safe to use. If you discover any issues, please do report them as a [GitHub Issue](https://github.com/nextauthjs/next-auth/issues) and we will patch them as soon as possible.
:::
This helper is helpful in case you want to read the session outside of the context of React.
When called, `getSession()` will send a request to `/api/auth/session` and returns a promise with a [session object](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/core/types.ts#L407-L425), or `null` if no session exists.

View File

@@ -1,6 +1,6 @@
{
"name": "next-auth",
"version": "4.18.10",
"version": "4.19.1",
"description": "Authentication for Next.js",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth.git",

View File

@@ -113,7 +113,7 @@ export default async function callbackHandler(params: {
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
sessionToken: await generateSessionToken(),
userId: user.id,
expires: fromDate(options.session.maxAge),
})
@@ -143,7 +143,7 @@ export default async function callbackHandler(params: {
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
sessionToken: await generateSessionToken(),
userId: userByAccount.id,
expires: fromDate(options.session.maxAge),
})
@@ -181,11 +181,11 @@ export default async function callbackHandler(params: {
? await getUserByEmail(profile.email)
: null
if (userByEmail) {
const provider = options.provider as OAuthConfig<any>;
const provider = options.provider as OAuthConfig<any>
if (provider?.allowDangerousEmailAccountLinking) {
// If you trust the oauth provider to correctly verify email addresses, you can opt-in to
// If you trust the oauth provider to correctly verify email addresses, you can opt-in to
// account linking even when the user is not signed-in.
user = userByEmail;
user = userByEmail
} else {
// We end up here when we don't have an account with the same [provider].id *BUT*
// we do already have an account with the same email address as the one in the
@@ -216,7 +216,7 @@ export default async function callbackHandler(params: {
session = useJwtSession
? {}
: await createSession({
sessionToken: generateSessionToken(),
sessionToken: await generateSessionToken(),
userId: user.id,
expires: fromDate(options.session.maxAge),
})

View File

@@ -1,5 +1,5 @@
import { openidClient } from "./client"
import { oAuth1Client } from "./client-legacy"
import { oAuth1Client, oAuth1TokenStore } from "./client-legacy"
import { createState } from "./state-handler"
import { createNonce } from "./nonce-handler"
import { createPKCE } from "./pkce-handler"
@@ -44,7 +44,7 @@ export default async function getAuthorizationUrl({
oauth_token_secret: tokens.oauth_token_secret,
...tokens.params,
})}`
oAuth1TokenStore.set(tokens.oauth_token, tokens.oauth_token_secret)
logger.debug("GET_AUTHORIZATION_URL", { url, provider })
return { redirect: url }
}

View File

@@ -1,6 +1,6 @@
import { TokenSet } from "openid-client"
import { openidClient } from "./client"
import { oAuth1Client } from "./client-legacy"
import { oAuth1Client, oAuth1TokenStore } from "./client-legacy"
import { useState } from "./state-handler"
import { usePKCECodeVerifier } from "./pkce-handler"
import { useNonce } from "./nonce-handler"
@@ -42,7 +42,7 @@ export default async function oAuthCallback(params: {
const { oauth_token, oauth_verifier } = query ?? {}
const tokens = (await (client as any).getOAuthAccessToken(
oauth_token,
null,
oAuth1TokenStore.get(oauth_token),
oauth_verifier
)) as TokenSet
let profile: Profile = await (client as any).get(
@@ -63,6 +63,8 @@ export default async function oAuthCallback(params: {
}
}
if (query?.oauth_token) oAuth1TokenStore.delete(query.oauth_token)
try {
const client = await openidClient(options)

View File

@@ -69,3 +69,5 @@ export function oAuth1Client(options: InternalOptions<"oauth">) {
}
return oauth1Client
}
export const oAuth1TokenStore = new Map()

View File

@@ -102,8 +102,8 @@ export default function ErrorPage(props: ErrorProps) {
}}
/>
)}
{theme?.logo && <img src={theme.logo} alt="Logo" className="logo" />}
<div className="card">
{theme?.logo && <img src={theme.logo} alt="Logo" className="logo" />}
<h1>{heading}</h1>
<div className="message">{message}</div>
{signin}

View File

@@ -49,6 +49,13 @@ export default function SigninPage(props: SignInServerPageParams) {
return false
})
if (typeof document !== "undefined" && theme.buttonText) {
document.documentElement.style.setProperty(
"--button-text-color",
theme.buttonText
)
}
if (typeof document !== "undefined" && theme.brandColor) {
document.documentElement.style.setProperty(
"--brand-color",
@@ -88,7 +95,17 @@ export default function SigninPage(props: SignInServerPageParams) {
}}
/>
)}
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
{theme.buttonText && (
<style
dangerouslySetInnerHTML={{
__html: `
:root {
--button-text-color: ${theme.buttonText}
}
`,
}}
/>
)}
<div className="card">
{error && (
<div className="error">
@@ -164,7 +181,7 @@ export default function SigninPage(props: SignInServerPageParams) {
placeholder="email@example.com"
required
/>
<button type="submit">Sign in with {provider.name}</button>
<button id="submitButton" type="submit">Sign in with {provider.name}</button>
</form>
)}
{provider.type === "credentials" && (

View File

@@ -23,13 +23,24 @@ export default function SignoutPage(props: SignoutProps) {
}}
/>
)}
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
{theme.buttonText && (
<style
dangerouslySetInnerHTML={{
__html: `
:root {
--button-text-color: ${theme.buttonText}
}
`,
}}
/>
)}
<div className="card">
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
<h1>Signout</h1>
<p>Are you sure you want to sign out?</p>
<form action={`${url}/signout`} method="POST">
<input type="hidden" name="csrfToken" value={csrfToken} />
<button type="submit">Sign out</button>
<button id="submitButton" type="submit">Sign out</button>
</form>
</div>
</div>

View File

@@ -22,8 +22,8 @@ export default function VerifyRequestPage(props: VerifyRequestPageProps) {
}}
/>
)}
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
<div className="card">
{theme.logo && <img src={theme.logo} alt="Logo" className="logo" />}
<h1>Check your email</h1>
<p>A sign in link has been sent to your email address.</p>
<p>

View File

@@ -465,7 +465,7 @@ export interface SessionOptions {
* However, you can specify your own custom string (such as CUID) to be used.
* @default `randomUUID` or `randomBytes.toHex` depending on the Node.js version
*/
generateSessionToken: () => string
generateSessionToken: () => Awaitable<string>
}
export interface DefaultUser {

View File

@@ -8,7 +8,8 @@
.__next-auth-theme-auto,
.__next-auth-theme-light {
--color-background: #fff;
--color-background: #ececec;
--color-background-card: #fff;
--color-text: #000;
--color-primary: #444;
--color-control-border: #bbb;
@@ -18,7 +19,8 @@
}
.__next-auth-theme-dark {
--color-background: #000;
--color-background: #161b22;
--color-background-card: #0d1117;
--color-text: #fff;
--color-primary: #ccc;
--color-control-border: #555;
@@ -29,7 +31,8 @@
@media (prefers-color-scheme: dark) {
.__next-auth-theme-auto {
--color-background: #000;
--color-background: #161b22;
--color-background-card: #0d1117;
--color-text: #fff;
--color-primary: #ccc;
--color-control-border: #555;
@@ -78,10 +81,9 @@ input[type] {
width: 100%;
padding: 0.5rem 1rem;
border: var(--border-width) solid var(--color-control-border);
background: var(--color-background);
background: var(--color-background-card);
font-size: 1rem;
border-radius: var(--border-radius);
box-shadow: inset 0 0.1rem 0.2rem rgba(0, 0, 0, 0.2);
color: var(--color-text);
&:focus {
@@ -107,41 +109,39 @@ a.button {
}
}
button span {
flex-grow: 1;
}
button,
a.button {
margin: 0 0 0.75rem 0;
padding: 0.75rem 1rem;
color: var(--provider-color, var(--color-primary));
background-color: var(--provider-bg, var(--color-background));
background-color: var(--provider-bg, var(--color-background-card));
font-size: 1.1rem;
min-height: 62px;
border-color: rgba(0, 0, 0, 0.1);
border-radius: var(--border-radius);
transition: all 0.1s ease-in-out;
box-shadow: #000 0px 0px 0px 0px, #000 0px 0px 0px 0px,
rgba(0, 0, 0, 0.2) 0px 10px 15px -3px, rgba(0, 0, 0, 0.1) 0px 4px 6px -4px;
font-weight: 500;
position: relative;
display: flex;
align-items: center;
justify-content: center;
&:has(img) {
justify-content: unset;
span {
flex-grow: 1;
}
@media (max-width: 450px) {
font-size: 0.9rem;
}
&:hover {
cursor: pointer;
}
&:active {
box-shadow: 0 0.15rem 0.3rem rgba(0, 0, 0, 0.15),
inset 0 0.1rem 0.2rem var(--color-background),
inset 0 -0.1rem 0.1rem rgba(0, 0, 0, 0.1);
cursor: pointer;
}
#provider-logo {
width: 25px;
display: block;
}
#provider-logo-dark {
@@ -149,20 +149,23 @@ a.button {
}
}
#submitButton {
color: var(--button-text-color, var(--color-info-text));
background-color: var(--brand-color, var(--color-info));
width: 100%;
}
@media (prefers-color-scheme: dark) {
button,
a.button {
color: var(--provider-dark-color, var(--color-primary));
background-color: var(--provider-dark-bg, var(--color-background));
border: 1px solid #0d0d0d;
box-shadow: #000 0px 0px 0px 0px, #ccc 0px 0px 0px 0px,
rgba(255, 255, 255, 0.01) 0px 5px 5px -3px,
rgba(255, 255, 255, 0.05) 0px 4px 6px -4px;
}
#provider-logo {
display: none !important;
}
#provider-logo-dark {
width: 25px;
display: block !important;
}
}
@@ -189,7 +192,6 @@ a.site {
> div {
text-align: center;
padding: 0.5rem;
}
}
@@ -217,16 +219,16 @@ a.site {
display: block;
border: 0;
border-top: 1px solid var(--color-seperator);
margin: 1.5em auto 0 auto;
margin: 2rem auto 1rem auto;
overflow: visible;
&::before {
content: "or";
background: var(--color-background);
background: var(--color-background-card);
color: #888;
padding: 0 0.4rem;
position: relative;
top: -0.6rem;
top: -0.7rem;
}
}
@@ -234,7 +236,7 @@ a.site {
background: #f5f5f5;
font-weight: 500;
border-radius: 0.3rem;
background: var(--color-info);
background: var(--color-error);
p {
text-align: left;
@@ -260,25 +262,26 @@ a.site {
max-width: 300px;
}
}
.signout {
.message {
margin-bottom: 1.5rem;
}
}
.logo {
display: inline-block;
margin-top: 100px;
max-width: 300px;
max-height: 150px;
max-width: 150px;
margin-top: 20px;
margin-bottom: 25px;
max-height: 70px;
}
.card {
max-width: max-content;
border: 1px solid var(--color-control-border);
border-radius: 5px;
@media screen and (min-width: 450px) {
width: 350px;
}
@media screen and (max-width: 450px) {
width: 200px;
}
margin: 20px 0 20px 0;
background-color: var(--color-background-card);
border-radius: 30px;
padding: 20px 50px;
margin: 50px auto;
.header {
color: var(--color-primary);
@@ -286,5 +289,5 @@ a.site {
}
.section-header {
color: var(--brand-color, var(--color-text));
color: var(--color-text);
}

View File

@@ -170,9 +170,14 @@ export async function getServerSession<
let deprecatedWarningShown = false
/** @deprecated renamed to `getServerSession` */
export async function unstable_getServerSession(
...args: Parameters<typeof getServerSession>
): ReturnType<typeof getServerSession> {
export async function unstable_getServerSession<
O extends GetServerSessionOptions,
R = O["callbacks"] extends { session: (...args: any[]) => infer U }
? U
: Session
>(
...args: Parameters<typeof getServerSession<O, R>>
): ReturnType<typeof getServerSession<O, R>> {
if (!deprecatedWarningShown && process.env.NODE_ENV !== "production") {
console.warn(
"`unstable_getServerSession` has been renamed to `getServerSession`."