OAuth2Strategy

一種用於使用和實作 OAuth2 框架的策略,以與 Google、Facebook、GitHub 等聯合服務進行身份驗證。

[!WARNING] 此策略預期身份提供者嚴格遵循 OAuth2 規範。如果提供者不遵循規範且偏離規範,此策略可能無法如預期般運作。

支援的執行環境

執行環境 是否支援
Node.js
Cloudflare

如何使用

安裝

npm add remix-auth-oauth2

直接安裝

您可以將此策略新增至您的驗證器實例並配置正確的端點來使用它。

import { OAuthStrategy, CodeChallengeMethod } from "remix-auth-oauth2";

export const authenticator = new Authenticator<User>();

authenticator.use(
  new OAuth2Strategy(
    {
      cookie: "oauth2", // Optional, can also be an object with more options

      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,

      authorizationEndpoint: "https://provider.com/oauth2/authorize",
      tokenEndpoint: "https://provider.com/oauth2/token",
      redirectURI: "https://example.app/auth/callback",

      tokenRevocationEndpoint: "https://provider.com/oauth2/revoke", // optional

      scopes: ["openid", "email", "profile"], // optional
      codeChallengeMethod: CodeChallengeMethod.S256, // optional
    },
    async ({ tokens, request }) => {
      // here you can use the params above to get the user and return it
      // what you do inside this and how you find the user is up to you
      return await getUser(tokens, request);
    }
  ),
  // this is optional, but if you setup more than one OAuth2 instance you will
  // need to set a custom name to each one
  "provider-name"
);

然後您需要設定您的路由,對於 OAuth2 流程,您需要呼叫兩次 authenticate 方法。

首先,您將使用在驗證器中設定的提供者名稱呼叫 authenticate 方法。

export async function action({ request }: Route.ActionArgs) {
  await authenticator.authenticate("provider-name", { request });
}

[!NOTE] 此路由可以是 actionloader,這取決於您是透過 POST 還是 GET 請求觸發流程。

這將啟動 OAuth2 流程,並將使用者重新導向到提供者的登入頁面。一旦使用者登入並授權您的應用程式,提供者會將使用者重新導向回您的應用程式重新導向 URI。

您現在需要在該 URI 上建立一個路由來處理來自提供者的回調。

export async function loader({ request }: Route.LoaderArgs) {
  let user = await authenticator.authenticate("provider-name", request);
  // now you have the user object with the data you returned in the verify function
}

[!NOTE] 此路由必須是一個 loader,因為重新導向將觸發一個 GET 請求。

一旦您取得策略驗證函數傳回的 user 物件,您可以對該資訊執行任何您想執行的操作。這可以是將使用者儲存在會話中、在資料庫中建立新使用者、將帳戶連結到資料庫中現有的使用者等。

使用刷新令牌

該策略公開了一個公共的 refreshToken 方法,您可以使用該方法來刷新存取令牌。

let strategy = new OAuth2Strategy<User>(options, verify);
let tokens = await strategy.refreshToken(refreshToken);

刷新令牌是驗證函數接收的 tokens 物件的一部分。您如何儲存它以呼叫 strategy.refreshToken 以及之後如何處理 tokens 物件取決於您。

最常見的方法是將刷新令牌儲存在使用者資料中,然後在刷新令牌後更新會話。

authenticator.use(
  new OAuth2Strategy<User>(
    options,
    async ({ tokens, request }) => {
      let user = await getUser(tokens, request);
      return {
        ...user,
        accessToken: tokens.accessToken()
        refreshToken: tokens.hasRefreshToken() ? tokens.refreshToken() : null,
      }
    }
  )
);

// later in your code you can use it to get new tokens object
let tokens = await strategy.refreshToken(user.refreshToken);

撤銷令牌

您可以撤銷使用者在提供者處擁有的存取令牌。

await strategy.revokeToken(user.accessToken);

探索提供者

如果您想要探索提供者的端點,您可以使用 discover 靜態方法。

export let authenticator = new Authenticator<User>();

authenticator.use(
  await OAuth2Strategy.discover<User>(
    "https://provider.com",
    {
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      redirectURI: "https://example.app/auth/callback",
      scopes: ["openid", "email", "profile"], // optional
    },
    async ({ tokens, request }) => {
      // here you can use the params above to get the user and return it
      // what you do inside this and how you find the user is up to you
      return await getUser(tokens, request);
    }
  )
);

這將擷取提供者的設定端點 (/.well-known/openid-configuration),並從中取得授權、令牌和撤銷端點,它還將擷取支援的程式碼挑戰方法,並嘗試使用 S256(如果支援)。

請記住,這將在建立策略時執行擷取,這將會增加您應用程式啟動的延遲時間。

建議只使用此方法一次,然後將端點複製到您的配置中。

您可以透過將一個物件傳遞給 cookie 選項來自訂 cookie 選項。

authenticator.use(
  new OAuth2Strategy<User>(
    {
      cookie: {
        name: "oauth2",
        maxAge: 60 * 60 * 24 * 7, // 1 week
        path: "/auth",
        httpOnly: true,
        sameSite: "lax",
        secure: process.env.NODE_ENV === "production",
      },
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      authorizationEndpoint: "https://provider.com/oauth2/authorize",
      tokenEndpoint: "https://provider.com/oauth2/token",
      redirectURI: "https://example.app/auth/callback",
    },
    async ({ tokens, request }) => {
      return await getUser(tokens, request);
    }
  )
);

這將使用名稱 oauth2 設定 cookie,最大期限為 1 週,僅可在 /auth 路徑上存取,僅限 http,同站 lax,並且如果應用程式在生產環境中執行則為安全。