while testing the library with entra id, I got the following error while trying to get the tokens:
AADSTS9002327: Tokens issued for the 'Single-Page Application' client-type may only be redeemed via cross-origin requests. Trace ID: <traceId> Correlation ID: <correlationId> Timestamp: 2024-03-22 09:42:30Z
My current workaround is to create my own token request.
Not sure if this is a problem with all providers, but based on what I have seen, it seems that EntraID needs the origin
header in the token request if we use a "public client".
Not sure if it helps, but here the code which I have used to test it.
( used hono via npm create hono@latest
and updated the src/index.ts
with the code below )
// required environment variables are:
// TENANT_ID and APP_ID
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
import { MicrosoftEntraId, generateState, generateCodeVerifier } from "arctic";
import got from "got";
import { inspect } from "util";
const app = new Hono();
const redirectUri = "http://localhost:3000/api/auth/callback";
const entraId = new MicrosoftEntraId(
process.env.TENANT_ID,
process.env.APP_ID,
"",
redirectUri
);
app.get("/", async (c) => {
const state = generateState();
const codeVerifier = generateCodeVerifier();
const url = await entraId.createAuthorizationURL(state, codeVerifier, {
scopes: ["openid", "profile", "email", `${process.env.APP_ID}/.default`],
});
// store state verifier as cookie
setCookie(c, "state", state, {
secure: false,
path: "/",
httpOnly: true,
maxAge: 60 * 10, // 10 min
});
// store code verifier as cookie
setCookie(c, "code_verifier", codeVerifier, {
secure: false,
path: "/",
httpOnly: true,
maxAge: 60 * 10, // 10 min
});
return c.html(`
<div>
<a href="${url}">Get access token</a>
</div>
`);
});
app.get("/api/auth/callback", async (c) => {
const code = c.req.query("code");
const state = c.req.query("state");
const storedState = getCookie(c, "state");
const storedCodeVerifier = getCookie(c, "code_verifier");
if (!code || !storedState || !storedCodeVerifier || state !== storedState) {
// 400
throw new Error("Invalid request");
}
/**
* since we can't (yet) set the origin for the token request
* we have to use our own implementation to get the tokens
* implementation is based on https://github.com/pilcrowOnPaper/oslo/blob/main/src/oauth2/index.ts
*/
// const tokens = await entraId.validateAuthorizationCode(
// code,
// storedCodeVerifier
// );
const tokenResponse = await got(
`https://login.microsoftonline.com/${env.TENANT_ID}/oauth2/v2.0/token`,
{
method: "post",
form: {
code: code,
client_id: process.env.APP_ID,
grant_type: "authorization_code",
redirect_uri: redirectUri,
code_verifier: storedCodeVerifier,
},
responseType: "json",
headers: {
origin: redirectUri,
},
throwHttpErrors: false,
}
);
deleteCookie(c, "code");
deleteCookie(c, "state");
if (!tokenResponse.ok) {
console.log({ error: tokenResponse.body });
return c.text(
"Something went wrong while fetching the tokens ( check the console for more details )"
);
}
return c.text(inspect(tokenResponse.body, { depth: 2 }));
});
const port = 3000;
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port,
});