Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am new to OIDC (OpenID Connect) and need guidance on how to implement an OIDC login flow in my Angular application with a Node.js backend. Below is my backend code for the OIDC flow, but I'm not sure how to integrate it into my Angular application correctly. Here's the flow of my application: The Angular frontend initiates Single Sign-On (SSO) with an OIDC server. It receives both an id_token and an access_token. The Angular frontend uses the id_token as an authentication token. Once the user is authorized to access the frontend application, the Angular app uses the access_token in the Authorization header to invoke the Node.js backend. The Node.js backend verifies the access_token. A token introspection API will call for token validation. After token introspection, another API call is made to retrieve user details. My confusion lies in how to orchestrate these API calls when my Angular page loads and after a successful login, redirect to the home page with user details. I'm not looking for code, but rather an understanding of how to architect this in my Angular application.

Could someone provide guidance on the architectural approach to integrating this OIDC login flow into my Angular application?

node js api end points provided

What I have tried:

JavaScript
// Authorization Endpoint

router.get('/AuthPage', generateRandomStateAndNonce, generateCodeVerifierAndChallenge, 
(req, res) => {
const scope = 'openid profile group_type authorization_group offline_access';
const redirectUri = encodeURIComponent(config.REDIRECT_URI);
const clientID = config.CLIENT_ID;

const authUrl = `${config.BASE_URL}${config.AUTHORIZATION_URL}? client_id=${clientID}&redirect_uri=${redirectUri}&scope=${scope}&state=${req.state}&nonce=${req.nonce}&response_type=code&code_challenge=${req.codeChallenge}&code_challenge_method=${codeChallengeMethod}`;

logger.info('Authorization URL generated', { authUrl });

res.cookie('XSRF-TOKEN', req.state);
res.cookie('nonce', req.nonce);
res.redirect(authUrl);
});
// Token Endpoint

router.post('/getAccessToken', async (req, res) => {
const state = req.headers['x-xsrf-token'];
try {
    const codeChallenge = req.body.code_challenge;
    const codeChallengeMethod = req.body.code_challenge_method;

    const response = await axios.post(`${config.BASE_URL}${config.TOKEN_URL}`, {
        client_id: config.clientID,
        client_secret: config.CLIENT_SECRET,
        code: req.body.code,
        redirect_uri: config.REDIRECT_URI,
        state: state,
        grant_type: 'authorization_code',
        code_challenge: codeChallenge, // Set code_challenge
        code_challenge_method: codeChallengeMethod // Set code_challenge_method
    });

    if (response.data.access_token) {
        const introspectionResponse = await  axios.post(`${config.BASE_URL}${config.INTROSPECTION_URL}`, {
            client_id: config.INTROSPECTION_CLIENT_ID,
            client_secret: config.INTROSPECTION_CLIENT_SECRET,
            token: response.data.access_token
        });

        if (introspectionResponse.data.active) {
            const idToken = response.data.id_token;

            // Verify ID Token
            const nonce = req.cookies.nonce;
            const idTokenHeader = JSON.parse(Buffer.from
                   (idToken.split('.')[0], 'base64').toString('utf-8'));
            const idTokenPayload = JSON.parse(Buffer.from
                   (idToken.split('.')[1], 'base64').toString('utf-8'));

            if (
                idTokenHeader.alg !== 'RS256' || // Ensure RS256 algorithm is used
                idTokenPayload.iss !== config.IDP_ISSUER || // Validate issuer
                idTokenPayload.aud !== config.CLIENT_ID || // Replace with your actual client ID
                idTokenPayload.nonce !== nonce || // Validate nonce
                idTokenPayload.exp < Math.floor(Date.now() / 1000) // Check expiration
            ) {
                res.status(401).send('Unauthorized');
            } else {
                // Access token and ID token are valid, 
                // proceed with the user details request
                req.session.token = response.data.access_token;
                res.send(response.data);
            }
        } else {
            // Access token is not valid, handle accordingly
            res.status(401).send('Unauthorized');
        }
    } else {
        res.status(401).send('Unauthorized');
    }
} catch (error) {
    console.error(error);
    res.status(500).send(error.message);
}
});
// UserInfo Endpoint

router.get('/getUserDetails', async (req, res) => {
if (req.session.token) {
    try {
        const response = await axios.get(`${config.BASE_URL}${config.USERINFO_URL}`, {
            headers: { Authorization: `Bearer ${req.session.token}` }
        });
        res.cookie('login', response.data.login, { httpOnly: true });

        // Log the user details
        logger.info('getUserDetails succeeded', { userDetails: response.data });

        res.send(response.data);
    } catch (error) {
        // Log the error
        logger.error('Error in getUserDetails', { error });

        res.status(500).send(error.message);
    }
} else {
    res.status(401).send('Unauthorized');
}
});
// Logout Endpoint

router.get('/logout', (req, res) => {
req.session.destroy();
res.clearCookie('XSRF-TOKEN');
res.clearCookie('login');
// Redirect to the specified LOGOUT_URI
res.redirect(config.LogoutURI);
});
Posted
Updated 15-Sep-23 3:20am
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900