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:
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);
});
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,
code_challenge_method: codeChallengeMethod
});
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;
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' ||
idTokenPayload.iss !== config.IDP_ISSUER ||
idTokenPayload.aud !== config.CLIENT_ID ||
idTokenPayload.nonce !== nonce ||
idTokenPayload.exp < Math.floor(Date.now() / 1000)
) {
res.status(401).send('Unauthorized');
} else {
req.session.token = response.data.access_token;
res.send(response.data);
}
} else {
res.status(401).send('Unauthorized');
}
} else {
res.status(401).send('Unauthorized');
}
} catch (error) {
console.error(error);
res.status(500).send(error.message);
}
});
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 });
logger.info('getUserDetails succeeded', { userDetails: response.data });
res.send(response.data);
} catch (error) {
logger.error('Error in getUserDetails', { error });
res.status(500).send(error.message);
}
} else {
res.status(401).send('Unauthorized');
}
});
router.get('/logout', (req, res) => {
req.session.destroy();
res.clearCookie('XSRF-TOKEN');
res.clearCookie('login');
res.redirect(config.LogoutURI);
});