Skip to content
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

Merged
merged 4 commits into from
Dec 16, 2020
Merged

OIDC proxy implementation #1383

merged 4 commits into from
Dec 16, 2020

Conversation

OtterleyW
Copy link
Contributor

@OtterleyW OtterleyW commented Dec 2, 2020

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.

// loginWithIdp endpoint in Flex API to authenticate user to Flex
router.get('/auth/linkedin/callback', authenticateLinkedinCallback);

router.get('/.well-known/openid-configuration', openIdConfiguration);
Copy link
Contributor

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-util/idToken.js Outdated Show resolved Hide resolved
server/api-util/idToken.js Outdated Show resolved Hide resolved
defaultConfirm,
};

console.log('userData:', userData);
Copy link
Contributor

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?

// 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');
Copy link
Contributor

@Gnito Gnito Dec 2, 2020

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?).

Copy link
Contributor Author

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.).

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@lyyder lyyder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

server/apiRouter.js Outdated Show resolved Hide resolved
Comment on lines 46 to 65
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);
Copy link
Contributor

@lyyder lyyder Dec 3, 2020

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.

Copy link
Contributor Author

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?

// 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 }] });
Copy link
Contributor

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.

Copy link
Contributor

@lyyder lyyder left a 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 Show resolved Hide resolved
server/api-util/idToken.js Outdated Show resolved Hide resolved
Comment on lines 95 to 104
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 });
Copy link
Contributor

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 });
  });

Copy link
Contributor Author

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.

@OtterleyW OtterleyW force-pushed the oidc-proxy branch 2 times, most recently from 6c8e7f7 to 41e8b23 Compare December 15, 2020 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants