Senario
I have a two angular Apps first one is a angular_shop
an which use /auth
endpoint for a custom node_oidc_provider to start auth request
node_oidc_provider the checks the PKCE code from angular_shop
and redirects to second angular app angular_oidc_frontend
which acts as a frontend of custom identity provider created using node_oidc_provider
I can see that the redirection to http:localhost:4200/signin?uid=WIkb3oodGHMOTgg4mlkyQ
is happening successfully
But now i have two problems
node_oidc_provider
is not visible in application tab of the chrome in first load of angular application, but it can be seen if i refresh the angular application page.sameSite
is set to None
the two cookie visible is not send to the server when i submit the login details to localhost:8081/api/v1/sigin
endpoind to the idp sever which is created using node_oidc_provider
Here is the request and response header to /signin
endpoint which i logged at server
Received headers: {
'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
'accept-encoding': 'gzip, deflate, br',
referer: 'http://localhost:4200/signin?uid=PbR6zZdrqGsDauDy-Sb1X',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
origin: 'http://localhost:4200',
'sec-ch-ua-platform': '"macOS"',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'sec-ch-ua-mobile': '?0',
'content-type': 'application/json',
accept: 'application/json, text/plain, */*',
'sec-ch-ua': '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"',
'content-length': '116',
connection: 'close',
host: 'localhost:8081'
}
Sent headers: [Object: null prototype] {
'x-powered-by': 'Express',
'access-control-allow-origin': 'http://localhost:4200',
'access-control-allow-methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'access-control-allow-headers': 'X-Requested-With,content-type,Authorization,skip',
'access-control-allow-credentials': 'true'
}
Note: initially i though this must be due to two different origin 'http://localhost:4200and
http://localhost:8081` so setup a proxy at angular cli with following setup
{
"/temp": {
"target": "http://localhost:8081",
"secure": false,
"pathRewrite": {
"^/temp": ""
},
"logLevel": "debug",
"changeOrigin":true,
"cookieDomainRewrite":"localhost"
}
}
Also the angular app is also configured to send the withCredentials: true
for /signin
endpoint.
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
};
Other configuration at the backend is as following
oidc-provide cookie-config:
cookies: {
keys: ['ewdewdewdewdewdwd', 'dewdewdwedwedwedewd', 'dwedwedwdwd'],
long: {
signed: true,
httpOnly: true,
maxAge: 86400 * 1000, // 1 day in milliseconds
// Add secure flag in production
secure: process.env.NODE_ENV === 'production',
// Add sameSite policy (strict, lax, or none)
sameSite: 'none',
},
short: {
signed: true,
httpOnly: true,
// Add secure flag in production
secure: process.env.NODE_ENV === 'production',
// Add sameSite policy (strict, lax, or none)
sameSite: 'none',
},
names: {
session: '_session',
interaction: '_interaction',
resume: '_resume',
state: '_state',
},
},
custom interaction policy created
const interactions = policy();
interactions.add(new Prompt({
name: 'signin',
requestable: true,
check: async (ctx) => {
// Here, you check if the user is already authenticated
// If not, you'll prompt for login
return !ctx.oidc.session || !ctx.oidc.session.accountId;
},
}), 0);
created policy modified to redirect to correct url
interactions: {
policy: interactions,
url(ctx, interaction) {console.info('interaction',interaction)
return `${process.env.IDENTITY_FRONTEND_URL}/signin?uid=${interaction.uid}`;
},
},
And here is my CROS config at the server
app.use((req, res, next) => {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
// Request method you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type,Authorization,skip');
// Set to true if you need the website to include cookies in the request sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', 'true');
// Pass to the next layer of middleware
next();
});
And my /signin
endpoint
signIn: async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.status(401).send('Authentication failed');
}
const interactionId = req.body.uid || req.query.uid || req.params.uid ;
if (!interactionId) {
return res.status(400).send('Interaction token is missing');
}
//console.info('req incoming',req);
const details = await oidcProvider.interactionDetails(req, res);
console.log(details);
assert.equal(details.interaction.uid, interactionId);
const { prompt: { name }, params, session } = details;
if (name === 'signin') {
// Assuming you have a method or a way to get accountId from req.user
const accountId = req.user._id.toString(); // Use a unique identifier for the user
const result = {
login: { accountId },
};
await oidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
} else {
res.status(400).send('Unsupported interaction type');
}
} catch (error) {
console.error(error);
res.status(500).send('An error occurred during authentication');
}
},
And for visibility here the error at server due to no cookie
being detected
{
allow_redirect: true,
error: 'invalid_request',
status: 400,
statusCode: 400,
expose: true,
error_description: 'interaction session id cookie not found',
error_detail: undefined
}
Also in production i plan to use nginx
to reverse proxy angular_oidc_frontend app and ipd nodejs api to serve from same domain to resolve the sameSite
cookie problem.
Advanced sorry for the long post, its a bit tricky to make short explanation for this complex flow between the apps, what i am implementing is ( Authorisation code flow ) oidc in custom idp (identity service provider) server created using node_oidc_provider
According to the 2dn "dev tools" screenshot, the cookie path is /signin
, meaning that it will only be sent with requests starting with localhost:4200/signin
But login details are sent to localhost:4200/api/v1/signin
instead so the cookie will be ignored
Changing the cookie path to /
should solve the problem.