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).
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.
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.