@auth/sveltekit
@auth/sveltekit 目前处于实验阶段。API *可能* 会发生变化。
SvelteKit Auth 是 Auth.js 的官方 SvelteKit 集成。它提供了一种简单的方法,只需几行代码即可将身份验证添加到您的 SvelteKit 应用程序中。
安装
npm install @auth/sveltekit用法
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/sveltekit/providers/github"
export const { handle, signIn, signOut } = SvelteKitAuth({
providers: [GitHub],
})延迟初始化
@auth/sveltekit 支持延迟初始化,您可以在其中读取 event 对象以延迟设置配置。当您需要从 event.platform 获取环境变量以用于 Cloudflare Workers 等平台时,这特别有用。
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/sveltekit/providers/github"
export const { handle, signIn, signOut } = SvelteKitAuth(async (event) => {
const authOptions = {
providers: [
GitHub({
clientId: event.platform.env.AUTH_GITHUB_ID,
clientSecret: event.platform.env.AUTH_GITHUB_SECRET
})
],
secret: event.platform.env.AUTH_SECRET,
trustHost: true
}
return authOptions
})在 src/hooks.server.ts 中重新导出 handle
export { handle } from "./auth"请记住设置 AUTH_SECRET 环境变量。这应该至少包含 32 个字符的随机字符串。在 UNIX 系统上,您可以使用 openssl rand -hex 32 或查看 https://generate-secret.vercel.app/32。
在将您的应用程序部署到 Vercel 以外的地方时,对于其他托管提供商(如 Cloudflare Pages 或 Netlify),将 AUTH_TRUST_HOST 变量设置为 true。
除非您覆盖 SvelteKitAuthConfig.basePath,否则 提供者 使用的回调 URL 必须设置为以下内容。
[origin]/auth/callback/[provider]登录和登出
服务器端
<SignIn /> 和 <SignOut /> 是 @auth/sveltekit 提供的开箱即用的组件 - 它们处理登录/登出流程,可以用作起点,也可以根据您的组件进行自定义。这是一个使用 SignIn 和 SignOut 组件通过 SvelteKit 的服务器端表单操作进行登录和登出的示例。您需要两件事才能使它工作
- 在您的 SvelteKit 应用程序前端使用这些组件
- 在
/signin(对于SignIn)和/signout(对于SignOut)添加所需的page.server.ts文件来处理表单操作
<script>
import { SignIn, SignOut } from "@auth/sveltekit/components"
import { page } from "$app/stores"
</script>
<h1>SvelteKit Auth Example</h1>
<div>
{#if $page.data.session}
{#if $page.data.session.user?.image}
<img
src={$page.data.session.user.image}
class="avatar"
alt="User Avatar"
/>
{/if}
<span class="signedInText">
<small>Signed in as</small><br />
<strong>{$page.data.session.user?.name ?? "User"}</strong>
</span>
<SignOut>
<div slot="submitButton" class="buttonPrimary">Sign out</div>
</SignOut>
{:else}
<span class="notSignedInText">You are not signed in</span>
<SignIn>
<div slot="submitButton" class="buttonPrimary">Sign in</div>
</SignIn>
<SignIn provider="facebook"/>
{/if}
</div>为了设置表单操作,我们需要在 src/routes 中定义文件
import { signIn } from "../../auth"
import type { Actions } from "./$types"
export const actions: Actions = { default: signIn }import { signOut } from "../../auth"
import type { Actions } from "./$types"
export const actions: Actions = { default: signOut }这些路由可以通过各自组件上的 signInPage 和 signOutPage 属性进行自定义。
客户端
我们还从 @auth/sveltekit/client 中导出两个方法,以便执行客户端登录和登出操作。
import { signIn, signOut } from "@auth/sveltekit/client"
<nav>
<p>
These actions are all using the methods exported from
<code>@auth/sveltekit/client</code>
</p>
<div class="actions">
<div class="wrapper-form">
<button on:click={() => signIn("github")}>Sign In with GitHub</button>
</div>
<div class="wrapper-form">
<button on:click={() => signIn("discord")}>Sign In with Discord</button>
</div>
<div class="wrapper-form">
<div class="input-wrapper">
<label for="password">Password</label>
<input
bind:value={password}
type="password"
id="password"
name="password"
required
/>
</div>
<button on:click={() => signIn("credentials", { password })}>
Sign In with Credentials
</button>
<button on:click={() => signOut()})}>
Sign Out
</button>
</div>
</div>
</nav>管理会话
上面的示例检查了在 $page.data.session 中可用的会话,但是我们需要在某处设置它。如果您希望此数据可用于所有路由,则可以将其添加到 src/routes/+layout.server.ts 中。以下代码在 $page 存储中设置会话数据,使其可用于所有路由。
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.auth()
};
};您在函数 LayoutServerLoad 中返回的内容将可用于 $page 存储的 data 属性中:$page.data。在本例中,我们返回一个包含 ‘session’ 属性的对象,这就是我们在其他代码路径中访问的属性。
处理授权
在 SvelteKit 中,您可以通过多种方法保护路由免受未经身份验证的用户的访问。
每个组件
最简单的情况是保护单个页面,在这种情况下,您应该将逻辑放在 +page.server.ts 文件中。请注意,在本例中,您还可以等待 event.parent 并从那里获取会话,但是即使您没有在根 +layout.server.ts 中执行上述操作,此实现也能正常工作
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async (event) => {
const session = await event.locals.auth();
if (!session?.user) throw redirect(303, '/auth');
return {};
};确保始终从父级获取会话信息,而不是在PageLoad的情况下使用存储。如果不这样做,可能会导致用户在+layout.server.ts未针对该页面加载运行的情况下,能够错误地访问受保护的信息。此代码示例已通过使用const { session } = await parent();来实现正确的方法。有关 SvelteKit 的 load 功能行为及其对身份验证的影响的更多信息,请参阅此SvelteKit 文档部分。
您不应该在+layout.server.ts中放置授权逻辑,因为该逻辑不能保证传播到树中的叶子节点。建议通过+page.server.ts文件手动保护每个路由,以避免错误。可以强制布局文件对所有路由运行加载函数,但这依赖于可能发生变化且难以检查的某些行为。有关这些注意事项的更多信息,请务必阅读 SvelteKit 存储库中的此问题:https://github.com/sveltejs/kit/issues/6315
按路径
另一种处理授权的方法是限制某些 URI 的可用性。对于许多项目来说,这更好,因为
- 这会自动保护这些 URI 中的操作和 API 路由。
- 组件之间没有代码重复。
- 非常容易修改。
通过 URI 处理授权的方法是覆盖您的处理挂钩。处理挂钩,由您在src/auth.ts中的SvelteKitAuth返回,是一个旨在接收发送到您的 SvelteKit Web 应用程序的所有请求的函数。您应该从src/auth.ts导出它,并将其导入到您的src/hooks.server.ts中。为了在您的hooks.server.ts中使用多个处理程序,我们可以使用 SvelteKit 的sequence来按顺序执行它们。
import { SvelteKitAuth } from '@auth/sveltekit';
import GitHub from '@auth/sveltekit/providers/github';
export const { handle, signIn, signOut } = SvelteKitAuth({
providers: [GitHub]
}),import { redirect, type Handle } from '@sveltejs/kit';
import { handle as authenticationHandle } from './auth';
import { sequence } from '@sveltejs/kit/hooks';
async function authorizationHandle({ event, resolve }) {
// Protect any routes under /authenticated
if (event.url.pathname.startsWith('/authenticated')) {
const session = await event.locals.auth();
if (!session) {
// Redirect to the signin page
throw redirect(303, '/auth/signin');
}
}
// If the request is still here, just proceed as normally
return resolve(event);
}
// First handle authentication, then authorization
// Each function acts as a middleware, receiving the request handle
// And returning a handle which gets passed to the next function
export const handle: Handle = sequence(authenticationHandle, authorizationHandle)在此了解更多关于 SvelteKit 的处理挂钩和序列的信息这里。
现在,/authenticated下的任何路由都将被处理挂钩透明地保护。您可以向序列添加更多类似中间件的函数,并在该文件中实现更复杂的授权业务逻辑。如果需要保护特定页面,并且通过 URI 进行保护可能存在缺陷,那么这也可以与基于组件的方法一起使用。
备注
- 如果您使用启用了
prerender的 SvelteKit 应用程序构建,则具有指向默认登录页面的锚标记的页面(例如<a href="/auth/signin" ...)将难以构建。请使用内置函数或组件登录或注销。
在此了解更多关于@auth/sveltekit的信息这里。
SvelteKitAuthConfig
AuthError
所有 Auth.js 错误的基本错误类。它经过优化,可以通过logger.error选项以格式良好的方式打印在服务器日志中。
扩展
构造函数
new AuthError(message, errorOptions)
new AuthError(message?, errorOptions?): AuthError参数
| 参数 | 类型 |
|---|---|
message? | string | ErrorOptions |
errorOptions? | ErrorOptions |
返回
覆盖
Error.constructor
属性
cause?
optional cause: Record<string, unknown> & {
err: Error;
};类型声明
err?
optional err: Error;覆盖
Error.cause
message
message: string;继承自
Error.message
name
name: string;继承自
Error.name
stack?
optional stack: string;继承自
Error.stack
type
type: ErrorType;错误类型。用于在日志中识别错误。
prepareStackTrace()?
static optional prepareStackTrace: (err, stackTraces) => any;用于格式化堆栈跟踪的可选覆盖。
见
https://v8.node.org.cn/docs/stack-trace-api#customizing-stack-traces
参数
| 参数 | 类型 |
|---|---|
err | Error |
stackTraces | CallSite[] |
返回
any
继承自
Error.prepareStackTrace
stackTraceLimit
static stackTraceLimit: number;继承自
Error.stackTraceLimit
方法
captureStackTrace()
static captureStackTrace(targetObject, constructorOpt?): void在目标对象上创建 .stack 属性。
参数
| 参数 | 类型 |
|---|---|
targetObject | object |
constructorOpt? | Function |
返回
void
继承自
Error.captureStackTrace
CredentialsSignin
可以从 Credentials 提供程序的 authorize 回调中抛出。当 authorize 回调中发生错误时,会发生两件事。
- 用户被重定向到登录页面,URL 中包含
error=CredentialsSignin&code=credentials。code是可配置的。 - 如果您在服务器端处理表单操作的框架中抛出此错误,则会抛出此错误,而不是重定向用户,因此您需要处理。
扩展
构造函数
new CredentialsSignin(message, errorOptions)
new CredentialsSignin(message?, errorOptions?): CredentialsSignin参数
| 参数 | 类型 |
|---|---|
message? | string | ErrorOptions |
errorOptions? | ErrorOptions |
返回
继承自
属性
cause?
optional cause: Record<string, unknown> & {
err: Error;
};类型声明
err?
optional err: Error;继承自
code
code: string;在重定向 URL 的 code 查询参数中设置的错误代码。
⚠ 注意:此属性将包含在 URL 中,因此请确保它不会暗示敏感错误。
如果需要调试,完整错误始终记录在服务器上。
通常,我们不建议具体提示用户是否输入了错误的用户名或密码,而是尝试使用“无效凭据”之类的提示。
message
message: string;继承自
name
name: string;继承自
stack?
optional stack: string;继承自
type
type: ErrorType;错误类型。用于在日志中识别错误。
继承自
kind
static kind: string;继承自
prepareStackTrace()?
static optional prepareStackTrace: (err, stackTraces) => any;用于格式化堆栈跟踪的可选覆盖。
参见
https://v8.node.org.cn/docs/stack-trace-api#customizing-stack-traces
参数
| 参数 | 类型 |
|---|---|
err | Error |
stackTraces | CallSite[] |
返回值
any
继承自
stackTraceLimit
static stackTraceLimit: number;继承自
类型
static type: string;方法
captureStackTrace()
static captureStackTrace(targetObject, constructorOpt?): void在目标对象上创建 .stack 属性。
参数
| 参数 | 类型 |
|---|---|
targetObject | object |
constructorOpt? | Function |
返回值
void
继承自
账户
通常包含有关使用提供者的信息,并且还扩展了 TokenSet,它是 OAuth 提供者返回的不同令牌。
扩展
Partial<TokenEndpointResponse>
属性
access_token?
optional readonly access_token: string;继承自
Partial.access_token
authorization_details?
optional readonly authorization_details: AuthorizationDetails[];继承自
Partial.authorization_details
expires_at?
optional expires_at: number;根据 TokenEndpointResponse.expires_in 计算的值。
它是 TokenEndpointResponse.access_token 过期的绝对时间戳(以秒为单位)。
此值可用于实现令牌轮换以及 TokenEndpointResponse.refresh_token。
参见
- https://authjs.oauth.ac.cn/guides/refresh-token-rotation#database-strategy
- https://www.rfc-editor.org/rfc/rfc6749#section-5.1
expires_in?
optional readonly expires_in: number;继承自
Partial.expires_in
id_token?
optional readonly id_token: string;继承自
Partial.id_token
提供者
provider: string;此账户的提供者 ID。例如,“google”。在 https://authjs.oauth.ac.cn/reference/core/providers 查看完整列表
providerAccountId
providerAccountId: string;此值取决于用于创建账户的提供者类型。
- oauth/oidc:OAuth 账户的 ID,从
profile()回调返回。 - 电子邮件:用户的电子邮件地址。
- 凭据:从
authorize()回调返回的id
refresh_token?
optional readonly refresh_token: string;继承自
Partial.refresh_token
范围?
optional readonly scope: string;继承自
Partial.scope
token_type?
optional readonly token_type: Lowercase<string>;注意:由于该值不区分大小写,因此始终以小写形式返回
继承自
Partial.token_type
类型
type: ProviderType;此账户的提供者类型
userId?
optional userId: string;此账户所属用户的 ID
参见
https://authjs.oauth.ac.cn/reference/core/adapters#adapteruser
DefaultSession
扩展自
属性
过期时间
expires: string;用户?
optional user: User;个人资料
从您的 OAuth 提供者返回的用户信息。
参见
https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
可索引
[claim: string]: unknown
属性
地址?
optional address: null | {
country: null | string;
formatted: null | string;
locality: null | string;
postal_code: null | string;
region: null | string;
street_address: null | string;
};出生日期?
optional birthdate: null | string;电子邮件?
optional email: null | string;电子邮件已验证?
optional email_verified: null | boolean;姓氏?
optional family_name: null | string;性别?
optional gender: null | string;名字?
optional given_name: null | string;ID?
optional id: null | string;区域设置?
optional locale: null | string;中间名?
optional middle_name: null | string;姓名?
optional name: null | string;昵称?
optional nickname: null | string;电话号码?
optional phone_number: null | string;图片?
optional picture: any;首选用户名?
optional preferred_username: null | string;个人资料?
optional profile: null | string;子?
optional sub: null | string;更新时间?
optional updated_at: null | string | number | Date;网站?
optional website: null | string;时区信息?
optional zoneinfo: null | string;会话
登录用户的活动会话。
扩展
属性
过期时间
expires: string;继承自
用户?
optional user: User;继承自
用户
OAuth 提供者在 profile 回调中返回的对象的形状,在 jwt 和 session 回调中可用,或者在使用数据库时,session 回调的第二个参数。
扩展自
属性
电子邮件?
optional email: null | string;ID?
optional id: string;图片?
optional image: null | string;姓名?
optional name: null | string;自定义Fetch
const customFetch: unique symbol;此选项允许您覆盖提供者使用的默认 fetch 函数,以直接向提供者的 OAuth 端点发出请求。使用不当会导致安全隐患。
它可用于支持公司代理、自定义提取库、缓存发现端点,添加用于测试、日志记录的模拟,为不符合规范的提供者设置自定义标头/参数等。
示例
import { Auth, customFetch } from "@auth/core"
import GitHub from "@auth/core/providers/github"
const dispatcher = new ProxyAgent("my.proxy.server")
function proxy(...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {
return undici(args[0], { ...(args[1] ?? {}), dispatcher })
}
const response = await Auth(request, {
providers: [GitHub({ [customFetch]: proxy })]
})参见
- https://undici.nodejs.org/#/docs/api/ProxyAgent?id=example-basic-proxy-request-with-local-agent-dispatcher
- https://authjs.oauth.ac.cn/guides/corporate-proxy
SvelteKitAuth()
SvelteKitAuth(config): {
handle: Handle;
signIn: Action;
signOut: Action;
}@auth/sveltekit 的主要入口点
参数
| 参数 | 类型 |
|---|---|
配置 | SvelteKitAuthConfig | (event) => PromiseLike<SvelteKitAuthConfig> |
返回值
{
handle: Handle;
signIn: Action;
signOut: Action;
}处理
handle: Handle;登录
signIn: Action;注销
signOut: Action;