@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
。
提供者使用的回呼 URL 必須設定為以下內容,除非您覆寫 SvelteKitAuthConfig.basePath
[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>
管理 Session
以上範例會檢查 $page.data.session
中是否有可用的 Session,不過這需要我們在某處進行設定。如果您希望所有路由都能使用此資料,您可以將其新增至 src/routes/+layout.server.ts
。以下程式碼會在 $page
儲存中設定 Session 資料,使其可供所有路由使用。
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 並從那裡取得 Session,但是即使您尚未在根 +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
的情況下,請務必**總是**從父元件取得 session 資訊,而不是使用 store 中的資訊。若不這樣做,可能會導致使用者在該頁面載入時,+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 中的 actions 和 api 路由
- 元件之間沒有重複的程式碼
- 非常容易修改
透過 URI 處理授權的方式是覆寫您的 handle hook。從 src/auth.ts
中的 SvelteKitAuth
返回的 handle hook 是一個函式,旨在接收發送到您的 SvelteKit 網路應用程式的所有請求。您應該從 src/auth.ts
匯出它,並將其匯入到您的 src/hooks.server.ts
中。若要在 hooks.server.ts
中使用多個 handle,我們可以使用 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 的 handle hooks 和 sequence 的資訊 這裡。
現在,/authenticated
下的所有路由都將由 handle hook 透明地保護。您可以向 sequence 中新增更多類似中介軟體的函式,並在此檔案中實作更複雜的授權業務邏輯。如果需要保護特定頁面,並且透過 URI 進行保護可能會出錯,也可以將其與基於元件的方法一起使用。
注意事項
- 如果您使用啟用
prerender
的方式來建置 SvelteKit 應用程式,則具有指向預設登入頁面錨點標籤(例如<a href="/auth/signin" ...
)的頁面將在建置時遇到問題。請改用 內建函式或元件來登入或登出。
在這裡了解更多關於 @auth/sveltekit
的資訊 這裡。
SvelteKitAuthConfig
重新匯出 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.dev.org.tw/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
回呼期間發生錯誤時,可能會發生兩種情況
- 使用者會被重新導向到登入頁面,網址中帶有
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;
錯誤類型。用於在記錄中識別錯誤。
繼承自
種類
static kind: string;
繼承自
prepareStackTrace()?
static optional prepareStackTrace: (err, stackTraces) => any;
格式化堆疊追蹤的可選覆寫
參見
https://v8.dev.org.tw/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.dev.org.tw/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.dev.org.tw/reference/core/providers 上的完整清單
providerAccountId
providerAccountId: string;
此值取決於用於建立帳戶的供應商類型。
- oauth/oidc:OAuth 帳戶的 ID,從
profile()
回呼返回。 - email:使用者的電子郵件地址。
- credentials:從
authorize()
回呼返回的id
refresh_token?
optional readonly refresh_token: string;
繼承自
Partial.refresh_token
scope?
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.dev.org.tw/reference/core/adapters#adapteruser
DefaultSession
擴展自
屬性
expires
expires: string;
user?
optional user: User;
個人資料
從您的 OAuth 供應商返回的使用者資訊。
參見
https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
可索引
[claim
: string
]: unknown
屬性
address?
optional address: null | {
country: null | string;
formatted: null | string;
locality: null | string;
postal_code: null | string;
region: null | string;
street_address: null | string;
};
birthdate?
optional birthdate: null | string;
email?
optional email: null | string;
email_verified?
optional email_verified: null | boolean;
family_name?
optional family_name: null | string;
gender?
optional gender: null | string;
given_name?
optional given_name: null | string;
id?
optional id: null | string;
locale?
optional locale: null | string;
middle_name?
optional middle_name: null | string;
name?
optional name: null | string;
nickname?
optional nickname: null | string;
phone_number?
optional phone_number: null | string;
picture?
optional picture: any;
preferred_username?
optional preferred_username: null | string;
profile?
optional profile: null | string;
sub?
optional sub: null | string;
updated_at?
optional updated_at: null | string | number | Date;
website?
optional website: null | string;
zoneinfo?
optional zoneinfo: null | string;
會期
已登入使用者的活動會期。
擴展自
屬性
expires
expires: string;
繼承自
user?
optional user: User;
繼承自
使用者
OAuth 提供者的 profile
回呼中傳回的物件形狀,可在 jwt
和 session
回呼中取得,或是當使用資料庫時,在 session
回呼的第二個參數中取得。
擴展自
屬性
email?
optional email: null | string;
id?
optional id: string;
image?
optional image: null | string;
name?
optional name: null | string;
customFetch
const customFetch: unique symbol;
此選項允許您覆寫提供者用來直接向提供者的 OAuth 端點發出請求的預設 fetch
函數。不正確地使用可能會造成安全隱患。
它可以被用來支援企業代理伺服器、自訂 fetch 函式庫、快取探索端點、為測試新增模擬資料、記錄、為不符合規範的提供者設定自訂標頭/參數等等。
範例
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.dev.org.tw/guides/corporate-proxy
SvelteKitAuth()
SvelteKitAuth(config): {
handle: Handle;
signIn: Action;
signOut: Action;
}
@auth/sveltekit
的主要進入點
參數
參數 | 類型 |
---|---|
config | SvelteKitAuthConfig | (event ) => PromiseLike <SvelteKitAuthConfig > |
回傳值
{
handle: Handle;
signIn: Action;
signOut: Action;
}
handle
handle: Handle;
signIn
signIn: Action;
signOut
signOut: Action;