Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Session Handling Issue with Microsoft Authentication in Modern.js #7410

Unanswered
os2mem asked this question in Q&A
Discussion options

Hello everyone,

We're implementing a micro-frontend architecture with module federation in our company and now require a JavaScript server for SSR features. We've been exploring Modern.js due to its excellent support for module federation, and I've found it straightforward to use. However, I've run into a challenge while testing server-side authentication using the Microsoft authentication library.

Problem:

I'm having trouble maintaining session values like sessionId or user tokens. Despite setting up most of the logic correctly, these session values seem to be lost between requests. I've tried using hono-sessions for session management, but the session data appears to be wiped out between requests.

Code Overview:

Here's a sample of the setup:

import { defineServerConfig } from "@modern-js/server-runtime";
import {
 ConfidentialClientApplication,
 Configuration,
 CryptoProvider,
} from "@azure/msal-node";
import { sessionMiddleware, CookieStore } from "hono-sessions";
import msalCachePlugin from "./cachePlugin";
const store = new CookieStore();
// scopes
const scopes = ["email", "openid"];
const msalConfig = {
 auth: {
 clientId: "<client-id>",
 authority: "<authority>",
 clientSecret: "<clientSecret>",
 scopes,
 knownAuthorities: ["<knownAuthorities>"],
 responseMode: "form_post",
 },
 cache: {
 cachePlugin: msalCachePlugin("./data/msal.cache.json"),
 },
} as Configuration;
const redirectUri = "<redirectUri>";
const pca = new ConfidentialClientApplication(msalConfig);
const cryptoProvider = new CryptoProvider();
const session = sessionMiddleware({
 store,
 encryptionKey: "password_at_least_32_characters_long", // Required for CookieStore, recommended for others.
 // You can also supply a function instead of a plain string
 // encryptionKey: () => 'function_that_returns_a_long_string'
 expireAfterSeconds: 900, // Expire session after 15 minutes of inactivity
 autoExtendExpiration: true, // Extend the session expiration time automatically. Defaults to true
 cookieOptions: {
 sameSite: "Lax", // Recommended for basic CSRF protection in modern browsers
 path: "/", // Required for this library to work properly
 httpOnly: true, // Recommended to avoid XSS attacks
 },
});
export default defineServerConfig({
 middlewares: [
 {
 name: "sessions",
 handler: session as any,
 before: ["auth-middleware"]
 },
 {
 name: "auth-middleware",
 handler: async (c, next) => {
 const session = c.get("session");
 const { code } = await c.req.parseBody();
 console.log("account", session.get("account"));
 const account = await pca
 .getTokenCache()
 .getAccountByHomeId(session.get("account")?.homeAccountId);
 console.log("getTokenCache", account);
 if (!code && !session.get("sessionId") && !account) {
 const sessionId = cryptoProvider.createNewGuid();
 session.set("sessionId", sessionId);
 console.log("session-setter", sessionId);
 console.log("auth-middleware", "sessionId", session.get("sessionId"));
 const r = await pca.getAuthCodeUrl({
 scopes,
 redirectUri,
 extraQueryParameters: {
 nativebroker: "1",
 },
 state: session.get("sessionId"),
 responseMode: "form_post",
 });
 return c.redirect(r);
 }
 await next();
 },
 },
 {
 name: "auth-middleware-redirect",
 path: "/redirect-login/",
 method: "post",
 handler: async (c, next) => {
 const { code, state } = await c.req.parseBody();
 const session = c.get("session");
 if (state) {
 if (state === session.get("sessionId")) {
 const tokenRequest = {
 code: code as string,
 scopes,
 redirectUri,
 enableSpaAuthorizationCode: true,
 tokenBodyParameters: {
 return_spa_code: "1",
 },
 };
 const token = await pca.acquireTokenByCode(tokenRequest);
 session.set("account", token.account);
 return c.redirect("/");
 }
 }
 return c.json({
 error: "Error",
 });
 },
 },
 ],
 renderMiddlewares: [],
 plugins: [],
});

Flow Description:

  • Initial Access: When a user accesses any page, middleware checks if they are authenticated.
  • Redirection: If not authenticated, the user is redirected to the identity provider's login page with a sessionId in the state parameter.
  • Post-authentication: After logging in, the user is redirected back to our app via a form post request, which includes a code for token acquisition and the state.
  • State Validation: Before requesting the token, the state must match the sessionId. However, upon redirection, the sessionId becomes null, indicating the session data is lost.

Without session logic, the app can authenticate successfully and store the token in a custom cache. I'm looking for insights or advice on what might be wrong or missing in my current setup.

Any help or suggestions would be greatly appreciated!

Thank you,

You must be logged in to vote

Replies: 1 comment

Comment options

The session here should be framework-independent. The issue is initially confirmed to be that sameSite: "Lax" prevents cross-origin POST requests from including cookies. You can try configuring it as sameSite: "None" and see if that works.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet
2 participants

AltStyle によって変換されたページ (->オリジナル) /