-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add proper roles and User service to JWT authorization
- Loading branch information
Showing
16 changed files
with
685 additions
and
192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Environment variables needed if you want to run `npm run dev` | ||
# and run the server locally, outside of Docker. For all the various | ||
# ways we run this using Docker, the env is defined by one of the | ||
# config/env.* files in the root. This requires that the other | ||
# services are running, particularly the login and users services. | ||
LOG_LEVEL=debug | ||
|
||
AUTH_PORT=7777 | ||
|
||
USERS_URL=http://localhost/v1/users | ||
|
||
# The Single Sign On (SSO) login service URL | ||
SSO_LOGIN_URL=http://localhost:8081/simplesaml/saml2/idp/SSOService.php | ||
|
||
# The callback URL endpoint to be used by the SSO login service (see the /auth route) | ||
SSO_LOGIN_CALLBACK_URL=http://localhost:7777/login/callback | ||
|
||
# The Single Logout (SLO) service URL | ||
SLO_LOGOUT_URL=http://localhost:8081/simplesaml/saml2/idp/SingleLogoutService.php | ||
|
||
# The callback URL endpoint to be used by the SLO logout service (see the /auth route) | ||
SLO_LOGOUT_CALLBACK_URL=http://localhost:7777/logout/callback | ||
|
||
# The SSO Identity Provider's public key certificate. NOTE: this is the public | ||
# key cert of the test login IdP docker container. Update for staging and prod. | ||
SSO_IDP_PUBLIC_KEY_CERT=MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ== | ||
|
||
# Our apps's Entity ID, which is also the URL to our metadata. | ||
SAML_ENTITY_ID=http://localhost:7777/sp | ||
|
||
# SECRET = cookie session SECRET. If left empty, one will be set automatically | ||
SECRET=secret-sauce | ||
|
||
# ADMINISTRATORS is a list (space delimited) of users who have administrator | ||
# rights. Use the user's nameID ([email protected]) or hashed version of | ||
# nameID (2b3b2b9ce8). Either will work. | ||
[email protected] | ||
|
||
# Origins of web apps that we'll allow for redirects. See src/api/auth/test | ||
ALLOWED_APP_ORIGINS=http://localhost:8000 http://localhost:8888 | ||
|
||
# The URI of the auth server | ||
JWT_ISSUER=http://localhost:7777 | ||
|
||
# The microservices origin | ||
JWT_AUDIENCE=http://localhost | ||
|
||
# How long should a JWT work before it expires | ||
JWT_EXPIRES_IN=1h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,12 @@ | |
* | ||
* https://blog.humphd.org/not-so-simple-saml/ | ||
*/ | ||
|
||
const passport = require('passport'); | ||
const SamlStrategy = require('passport-saml').Strategy; | ||
const { logger, hash } = require('@senecacdot/satellite'); | ||
const { createServiceToken, logger, hash } = require('@senecacdot/satellite'); | ||
const fetch = require('node-fetch'); | ||
|
||
const User = require('./user'); | ||
const Admin = require('./admin'); | ||
|
||
const { | ||
SAML_ENTITY_ID, | ||
|
@@ -17,6 +17,7 @@ const { | |
SSO_LOGIN_CALLBACK_URL, | ||
SLO_LOGOUT_URL, | ||
SLO_LOGOUT_CALLBACK_URL, | ||
USERS_URL, | ||
} = process.env; | ||
|
||
/** | ||
|
@@ -29,7 +30,8 @@ if ( | |
SSO_LOGIN_URL && | ||
SSO_LOGIN_CALLBACK_URL && | ||
SLO_LOGOUT_URL && | ||
SLO_LOGOUT_CALLBACK_URL | ||
SLO_LOGOUT_CALLBACK_URL && | ||
USERS_URL | ||
) | ||
) { | ||
logger.error( | ||
|
@@ -41,17 +43,7 @@ passport.serializeUser((user, done) => { | |
done(null, user); | ||
}); | ||
passport.deserializeUser((user, done) => { | ||
/** | ||
* We need to rehydrate a full user Object, using one of User or Admin. To do | ||
* so we determine this based on the current user's id (i.e. nameID in SAML) | ||
* matching what we have set in the env for ADMINISTRATORS. There can be | ||
* more than one admin user. | ||
*/ | ||
if (Admin.isAdmin(user.id)) { | ||
done(null, new Admin(user.name, user.email, user.id, user.nameID, user.nameIDFormat)); | ||
} else { | ||
done(null, new User(user.name, user.email, user.id, user.nameID, user.nameIDFormat)); | ||
} | ||
done(null, User.parse(user)); | ||
}); | ||
|
||
// Setup SAML authentication strategy | ||
|
@@ -67,15 +59,23 @@ const strategy = new SamlStrategy( | |
disableRequestedAuthnContext: true, | ||
signatureAlgorithm: 'sha256', | ||
}, | ||
(profile, done) => { | ||
if (!profile) { | ||
const error = new Error('SAML Strategy verify callback missing user profile'); | ||
function (senecaProfile, done) { | ||
if ( | ||
!( | ||
senecaProfile && | ||
senecaProfile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'] | ||
) | ||
) { | ||
const error = new Error( | ||
'SAML Strategy verify callback missing user profile and emailaddress claim' | ||
); | ||
logger.error({ error }); | ||
return done(error); | ||
done(error); | ||
return; | ||
} | ||
|
||
/** | ||
* The object we get back from Seneca takes this form: | ||
* The profile object we get back from Seneca takes this form: | ||
* { | ||
* "issuer": "https://sts.windows.net/...", | ||
* "inResponseTo": "_851650d2472d2921c6ac", | ||
|
@@ -107,17 +107,45 @@ const strategy = new SamlStrategy( | |
* "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "[email protected]" | ||
* "email": "[email protected]" | ||
* } | ||
* | ||
* We only really care about the emailaddress claim, which we also use in the Users service. | ||
*/ | ||
const email = | ||
senecaProfile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']; | ||
|
||
async function lookupTelescopeUser(id) { | ||
// We have the user's id (i.e. their hashed email address). Now | ||
// make a service-to-service request to the Users service in order to | ||
// get this user's full profile information using the user's id. They | ||
// may or may not have a Telescope profile (yet). | ||
try { | ||
const res = await fetch(`${USERS_URL}/${id}`, { | ||
headers: { | ||
Authorization: `bearer ${createServiceToken()}`, | ||
}, | ||
}); | ||
if (!res.ok) { | ||
if (res.status === 404) { | ||
// No Telescope user profile found, so create a regular Seneca user | ||
logger.debug({ senecaProfile }, `No Telescope account for ${id} with Users service`); | ||
done(null, new User(senecaProfile)); | ||
return; | ||
} | ||
// We can't get a response from the Users service, so we don't know what we have. | ||
throw new Error(`unable to get user info from Users service: ${res.status}`); | ||
} | ||
// If we get back profile data from the Users service, parse and use | ||
const telescopeProfile = await res.json(); | ||
logger.debug({ senecaProfile, telescopeProfile }, 'Telescope user authenticated'); | ||
done(null, new User(senecaProfile, telescopeProfile)); | ||
} catch (err) { | ||
console.log({ err }); | ||
logger.error({ err }); | ||
done(err, false); | ||
} | ||
} | ||
|
||
// We only use the displayname, emailaddress, and nameID info (hashed, for use in our db) | ||
return done(null, { | ||
// Include nameID so we can use it for Logout Requests back to the IdP. | ||
nameID: profile.nameID, | ||
nameIDFormat: profile.nameIDFormat, | ||
name: profile['http://schemas.microsoft.com/identity/claims/displayname'], | ||
email: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'], | ||
id: hash(profile.nameID), | ||
}); | ||
lookupTelescopeUser(hash(email)); | ||
} | ||
); | ||
passport.use(strategy); | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.