-
Notifications
You must be signed in to change notification settings - Fork 943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OIDC proxy implementation #1383
Conversation
server/apiRouter.js
Outdated
// loginWithIdp endpoint in Flex API to authenticate user to Flex | ||
router.get('/auth/linkedin/callback', authenticateLinkedinCallback); | ||
|
||
router.get('/.well-known/openid-configuration', openIdConfiguration); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to have some comment or even link somewhere, where one can read more about these routes that this setup requires.
server/api/auth/linkedin.js
Outdated
defaultConfirm, | ||
}; | ||
|
||
console.log('userData:', userData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this contain something that should not be saved to server logs in production?
server/api/auth/linkedin.js
Outdated
// to log in the user to Flex with the data from Linkedin | ||
exports.authenticateLinkedinCallback = (req, res, next) => { | ||
passport.authenticate('linkedin', function(err, user) { | ||
loginWithIdp(err, user, req, res, 'client-id', 'linkedin'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm quite confused about how this 'client-id' string works.
In loginWithIdp.js, there's CLIENT_ID constant and clientId param and they are not the same. There probably should be some kind of renaming made to these client id variables (and values?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial idea was that we are not going to keep the LinkedIn implementation in FTW so I cut some corners when I did testing. So this is one of those parts. But if we want to keep this in the repo, then this of course needs to be fixed.
Anyway, this 'client-id' is just a hard-coded test value. This 'client-id' the same thing you should have in process.env.CUSTOM_OIDC_CLIENT_ID and it's the value that should match the client id you have in Console. Most likely that environment variable is named to match the identity provider you are using (LinkedIn, Instagram, your own system etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense 👍
How about the renaming of things in loginWithIdp.js file? (And sorry, this should have been addressed already in previous PRs.) In that file, there is CLIENT_ID referring to Flex client id and "clientId" referring to IDP client id - and then code like clientId: CLIENT_ID,
- so, I'd imagine that this could be pretty confusing for someone who tries to understand what happens under the hood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really good point and I think renaming that parameter makes sense. I added that change to this PR now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
server/api-util/idToken.js
Outdated
const jwt = new SignJWT({ | ||
given_name: profile.firstName, | ||
family_name: profile.lastName, | ||
email: profile.email, | ||
email_verified: profile.emailVerified, | ||
}) | ||
.setProtectedHeader({ alg: 'RS256' }) | ||
.setIssuedAt() | ||
.setIssuer(issuerUrl) | ||
.setSubject(userId) | ||
.setAudience(clientId) | ||
.setExpirationTime('1h') | ||
.sign(privateKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change request: Did we have the kid
value in the token header? I might have mentioned that it's not compulsory but maybe we could add it by default to the ID token. Even though it's one more random value for the user to decide, enforcing the kid
value in the token and in JWK could be useful as key caching in the Flex API relies heavily on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we had but then I removed it to test if it works without it (and it did). I didn't want to leave it there with any hardcoded value. But I think it makes sense to have it in the example so it there again. Now the key id is also read from environment variables so it can be basically any string user decides. I guess this is ok?
server/api-util/idToken.js
Outdated
// api/.well-known/jwks.json endpoint as stated in discovery document | ||
exports.jwksUri = (req, res) => { | ||
fromKeyLike(crypto.createPublicKey(rsaPublicKey)).then(jwkPublicKey => { | ||
res.json({ keys: [{ alg: 'RS256', use: 'sig', ...jwkPublicKey }] }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about he kid
attribute.
03e16e0
to
82c2767
Compare
82c2767
to
032b3f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
server/api-util/idToken.js
Outdated
const jwkKeys = keys.map(key => { | ||
return fromKeyLike(crypto.createPublicKey(key.rsaPublicKey)); | ||
}); | ||
|
||
Promise.all(jwkKeys).then(resolvedJwkKeys => { | ||
const jwkKeys = resolvedJwkKeys.map((resolvedKey, i) => { | ||
const originalKey = keys[i]; | ||
return { alg: signingAlg, kid: originalKey.keyId, use: 'sig', ...resolvedKey }; | ||
}); | ||
res.json({ keys: jwkKeys }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the index access be avoided if this was was written somehow like this:
const jwkKeys = keys.map(key => {
return fromKeyLike(crypto.createPublicKey(key.rsaPublicKey))
// reads alg from key
.then(res => {
return {alg: key.alg, kid: key.keyId, ...res};
});
});
Promise.all(jwkKeys).then(resolvedJwkKeys => {
res.json({ keys: resolvedJwkKeys });
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the example! I tried to make it work like this yesterday but I might have missed something and the index access was the only way I managed to generate the list correctly. However, now it works like this and I agree that this is a better approach.
6c8e7f7
to
41e8b23
Compare
41e8b23
to
67fd190
Compare
67fd190
to
777ad3a
Compare
In this PR, we add helper functions for using FTW as OIDC proxy. These new helpers are added in the
api-util/idToken.js
file. This also introduces two now server-side endpoints for serving discovery document and jwks uri.