AWS OpenAuth React SPA
This is a full-stack monorepo app shows the OpenAuth flow for a single-page app and an authenticated API. It has:
-
React SPA built with Vite and the
StaticSitecomponent in thepackages/webdirectory.infra/web.ts export const web = new sst.aws.StaticSite("MyWeb", {path: "packages/web",build: {output: "dist",command: "npm run build",},environment: {VITE_API_URL: api.url,VITE_AUTH_URL: auth.url,},}); -
API with Hono and the
Functioncomponent inpackages/functions/src/api.ts.infra/api.ts export const api = new sst.aws.Function("MyApi", {url: true,link: [auth],handler: "packages/functions/src/api.handler",}); -
OpenAuth with the
Authcomponent inpackages/functions/src/auth.ts.infra/auth.ts export const auth = new sst.aws.Auth("MyAuth", {issuer: "packages/functions/src/auth.handler",});
The React frontend uses a AuthContext provider to manage the auth flow.
<AuthContext.Provider value={{ login, logout, userId, loaded, loggedIn, getToken, }}> {children}</AuthContext.Provider>Now in App.tsx, we can use the useAuth hook.
const auth = useAuth();
return !auth.loaded ? ( <div>Loading...</div>) : ( <div> {auth.loggedIn ? ( <div> <p> <span>Logged in</span> {auth.userId && <span> as {auth.userId}</span>} </p> </div> ) : ( <button onClick={auth.login}>Login with OAuth</button> )} </div>);Once authenticated, we can call our authenticated API by passing in the access token.
await fetch(`${import.meta.env.VITE_API_URL}me`, { headers: { Authorization: `Bearer ${await auth.getToken()}`, },});The API uses the OpenAuth client to verify the token.
const authHeader = c.req.header("Authorization");const token = authHeader.split(" ")[1];const verified = await client.verify(subjects, token);The sst.config.ts dynamically imports all the infra/ files.
await import("./infra/auth");await import("./infra/api");await import("./infra/web");View the full example.