[]previous and next page

Access tokens, refresh tokens and userinfo

accessToken(event) returns the stored access token, and exchanges the refresh token first when it expires within the next 30 seconds (leewaySeconds).

For agentsUpdated

accessToken(event) returns the stored access token, and exchanges the refresh token first when it expires within the next 30 seconds (leewaySeconds). Providers rotate refresh tokens: the old one stops working once a new one is issued. The client stores the new one, and calls that arrive together with the same token share one exchange (and a request that started before the first one saved its session reuses the result for 30 seconds), so parallel requests do not lose the grant. When the provider answers invalid_grant, the stored tokens are removed and the call fails with OIDC_REFRESH_FAILED: send the visitor through sign-in again.

src/routes/api/orders.tsts
import { isOidcError } from "@shadow-js/dusk/auth";
import { HttpError, json, type RequestEvent } from "@shadow-js/dusk/server";
import { oidc } from "../../auth";

export const GET = async (event: RequestEvent): Promise<Response> => {
  try {
    const token = await oidc.accessToken(event);
    if (token === undefined) throw new HttpError(401, "Sign in first");
    const answer = await fetch("https://api.example.com/orders", {
      headers: { authorization: `Bearer ${token}` },
    });
    return json(await answer.json());
  } catch (error) {
    if (isOidcError(error) && error.code === "OIDC_REFRESH_FAILED") {
      throw new HttpError(401, "Your sign-in ended; sign in again");
    }
    throw error;
  }
};

Request offline_access (or the provider's equivalent) to get a refresh token at all. userinfo(event) reads the userinfo endpoint with the stored access token and checks that the answer is for the signed-in user.