0

I am trying to build an auth server that logs in the user to other applications with the user's Microsoft account. I have created an app in Azure AD and integrated it with the backend. The following is the flow of the application,

  1. GET request to /auth/sign endpoint returns the Auth URL to the client generating a csrf_token which is stored in a session and a state parameter that is sent as a query param of Auth URL.
  2. With the auth URL https://login.microsoft.com/... a GET request is sent to Azure AD from the client.
  3. Once the user is logged in successfully with the credentials, a POST request is sent to the REDIRECT_URI configured in the Azure AD portal http://localhost:3000/auth/redirect in this case.

I get no error when I directly hit the endpoints in the browser. That is, http://localhost:3000/auth/signin returns the auth URL which is then used to get the details of the user through REDIRECT_URL.

When the frontend comes into action (Angular), I get the error that csrf_token is not matching as the session is undefined. I tried logging MemoryStore and the session was not expired and present in the store. Now, I have to validate the csrf_token in the request from the Azure AD.

Below is the pictorial representation of the issue I face.

Azure AD based login

The auth.js file for sign in,

router.get('/auth/signin', async function (req, res, next) {

    req.session.csrfToken = cryptoProvider.createNewGuid(); // Generate csrf and store in the session

    const state = cryptoProvider.base64Encode(
        JSON.stringify({
            csrfToken: req.session.csrfToken,

        })
    );

    const authCodeUrlRequestParams = {
        state: state,
        scopes: [],
    };

    const authCodeRequestParams = {
        scopes: [],
    };
     
    return res.json({authUrl: await redirectToAuthCodeUrl(req, res, next, authCodeUrlRequestParams, authCodeRequestParams)});
});

async function redirectToAuthCodeUrl(req, res, next, authCodeUrlRequestParams, authCodeRequestParams) {

    // Generate PKCE Codes before starting the authorization flow
    const { verifier, challenge } = await cryptoProvider.generatePkceCodes();

    // Set generated PKCE codes and method as session vars
    req.session.pkceCodes = {
        challengeMethod: 'S256',
        verifier: verifier,
        challenge: challenge,
    };

    req.session.authCodeUrlRequest = {
        redirectUri: REDIRECT_URI,
        responseMode: 'form_post', // recommended for confidential clients
        codeChallenge: req.session.pkceCodes.challenge,
        codeChallengeMethod: req.session.pkceCodes.challengeMethod,
        ...authCodeUrlRequestParams,
    };

    req.session.authCodeRequest = {
        redirectUri: REDIRECT_URI,
        code: "",
        ...authCodeRequestParams,
    };
   
    try {
        const authCodeUrlResponse = await msalInstance.getAuthCodeUrl(req.session.authCodeUrlRequest);
        
        return authCodeUrlResponse;
    } catch (error) {
        next(error);
    }
};

The /auth/redirect endpoint,

router.post('/auth/redirect', async function (req, res, next) {
    console.log(req);
    if (req.body.state) {
        const state = JSON.parse(cryptoProvider.base64Decode(req.body.state));
        
        // check if csrfToken matches
        if (state.csrfToken === req.session.csrfToken) {
            req.session.authCodeRequest.code = req.body.code; // authZ code
            req.session.authCodeRequest.codeVerifier = req.session.pkceCodes.verifier // PKCE Code Verifier

            try {
                const tokenResponse = await msalInstance.acquireTokenByCode(req.session.authCodeRequest);
                req.session.accessToken = tokenResponse.accessToken;
                req.session.idToken = tokenResponse.idToken;
                req.session.account = tokenResponse.account;
                req.session.isAuthenticated = true;
                return res.json({ session: req.session });
            } catch (error) {
                next(error);
            }
        } else {
            next(new Error('csrf token does not match'));
        }
    } else {
        next(new Error('state is missing'));
    }
});

Kindly help me in getting the details of the user from Azure AD and redirect it to the client end.

TIA

Kannan G R
  • 15
  • 5

0 Answers0