remix-auth-oauth2

repository·main·Indexed 42 days ago

https://github.com/sergiodxa/remix-auth-oauth2

A Remix authentication strategy for implementing the OAuth2 framework, enabling integration with federated services like Google, Facebook, and GitHub. Version 3.4.1 provides support for PKCE, token refreshing, token revocation, and OIDC discovery via the OAuth2Strategy.discover() method.

Tokens
3.1K
Snippets
11
Records
15
Agent score
2%

What's inside remix-auth-oauth2

  1. Configure and use OAuth2Strategy directly

    main

    To use the strategy, add it to your Authenticator instance. You must provide the client credentials and the provider's OAuth2 endpoints. The second argument to the OAuth2Strategy constructor is a verify function that receives tokens and request, where you should implement your user lookup logic.

    import { OAuth2Strategy, 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
          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"
    );
  2. Implement the OAuth2 authentication flow in Remix routes

    main

    The OAuth2 flow requires calling authenticator.authenticate twice: once to initiate the redirect to the provider, and once in the callback route to handle the response.

    1. Initiation Route: Call authenticate in an action (for POST) or loader (for GET). This redirects the user to the provider.
    2. Callback Route: Create a route matching your redirectURI. This must be a loader because the provider redirects via a GET request. Calling authenticate here will process the code and return the user object.
    // 1. Initiation Route
    export async function action({ request }: Route.ActionArgs) {
      await authenticator.authenticate("provider-name", request);
    }
    
    // 2. Callback Route (must be a loader)
    export async function loader({ request }: Route.LoaderArgs) {
      let user = await authenticator.authenticate("provider-name", request);
      // user is now available
    }
  3. Customize OAuth2 cookie options

    main

    You can pass an object to the cookie option in the OAuth2Strategy configuration to customize the session cookie (e.g., name, maxAge, path, httpOnly, sameSite, secure).

    new OAuth2Strategy(
      {
        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,
        // ... other options
      },
      async ({ tokens, request }) => { ... }
    )
  4. Refresh access tokens using refreshToken()

    main

    The OAuth2Strategy exposes a refreshToken method to obtain new tokens using a stored refresh token. The refresh token is available via tokens.refreshToken() inside the strategy's verify function.

    // Inside your verify function, you might return the refresh token as part of the user object
    async ({ tokens, request }) => {
      let user = await getUser(tokens, request);
      return {
        ...user,
        accessToken: tokens.accessToken(),
        refreshToken: tokens.hasRefreshToken() ? tokens.refreshToken() : null,
      };
    }
    
    // Later, to refresh:
    let strategy = new OAuth2Strategy<User>(options, verify);
    let tokens = await strategy.refreshToken(user.refreshToken);
  5. Discover provider endpoints using OAuth2Strategy.discover()

    main

    Instead of manually providing all endpoints, you can use the static discover method. It fetches the provider's configuration from /.well-known/openid-configuration to automatically populate authorization, token, and revocation endpoints. It also attempts to use S256 for code challenges if supported.

    Warning: This performs a network fetch during strategy creation, which adds latency to your application startup. It is recommended to use this once to find the endpoints, then hardcode them in your configuration.

    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"],
        },
        async ({ tokens, request }) => {
          return await getUser(tokens, request);
        }
      )
    );
  6. Configure OAuth2Strategy options

    main

    When instantiating OAuth2Strategy, you provide a ConstructorOptions object.

    Required Fields:

    • clientId: The Client ID provided by your Identity Provider.
    • clientSecret: The Client Secret (can be null for public clients).
    • authorizationEndpoint: The URL to redirect users to for login.
    • tokenEndpoint: The URL used to exchange codes for tokens.

    Optional Fields:

    • cookie: Customizes the cookie used for state and PKCE. Can be a string (the name) or an object containing a name and other SetCookieInit options. Defaults to "oauth2".
    • redirectURI: The URL where the provider redirects the user. Defaults to null (the current URL is used if not provided).
    • tokenRevocationEndpoint: The endpoint used to revoke tokens.
    • scopes: An array of strings representing requested permissions.
    • codeChallengeMethod: The PKCE method. Defaults to CodeChallengeMethod.S256.
    • audience: A string or array of strings representing the requested audience.
  7. Refresh OAuth2 tokens

    main

    You can obtain a new set of tokens using a refresh token by calling the refreshToken method on your strategy instance. This is useful when an access token has expired.

    let tokens = await strategy.refreshToken(refreshToken);
    console.log(tokens.accessToken());
  8. Use OAuth2Strategy for authentication

    main

    The OAuth2Strategy class is a Remix Auth strategy used to implement OAuth2 authentication flows. It handles the redirection to the identity provider, state/PKCE management via cookies, and token exchange.

    To use it, you instantiate the class with ConstructorOptions and a verify function. The verify function is called after a successful token exchange and is responsible for returning the user object.

    import { OAuth2Strategy } from "remix-auth-oauth2";
    
    const strategy = new OAuth2Strategy(
      {
        clientId: "your-client-id",
        clientSecret: "your-client-secret",
        authorizationEndpoint: new URL("https://provider.com/auth"),
        tokenEndpoint: new URL("https://provider.com/token"),
        redirectURI: new URL("https://your-app.com/callback"),
        scopes: ["openid", "email"],
      },
      async ({ request, tokens }) => {
        // Use tokens.access_token to fetch user profile
        const user = await fetchUserProfile(tokens.access_token);
        return user;
      }
    );
  9. Discover an OAuth2 provider using OIDC

    main

    If your provider supports OpenID Connect (OIDC), you can use the static OAuth2Strategy.discover method. This method automatically fetches the configuration from the provider's .well-known/openid-configuration endpoint, so you don't have to manually specify endpoints like authorizationEndpoint or tokenEndpoint.

    import { OAuth2Strategy } from "remix-auth-oauth2";
    
    let strategy = await OAuth2Strategy.discover(
      "https://accounts.google.com",
      {
        clientId: "your-client-id",
        clientSecret: "your-client-secret",
        redirectURI: "https://your-app.com/auth/callback",
        scopes: ["openid", "email", "profile"],
      },
      async ({ tokens }) => {
        return getUserProfile(tokens.access_token);
      },
    );
  10. Revoke an OAuth2 token

    main

    If you have configured a tokenRevocationEndpoint in your strategy options, you can use the revokeToken method to invalidate an access token at the identity provider.

    // Get it from where you stored it
    let accessToken = await getAccessToken();
    await strategy.revokeToken(tokens.access_token);