forked from DevelopingSpace/starchart
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DevelopingSpace#195 SSO and SLO additions (DevelopingSpace#236)
* SAML Server, login callback, login page change, sp metadata route, small .env change SSO and SLO with SimpleSAMLPhp and Samlify Added SLO callback * linting concerns * else * POST check first, sAMAccountName extract, idp and sp only used in samlServer, new samlServer functions to export funcionality without exposing sp/idp * Add idp config locally * cleaned up the code comments, swaped the conditional logic to make some guard clauses, added file to metadata file name * forgot a console log * I think this addresses most things * Metadata secret check and remove fs in saml server, tried to add the relayState stuff in the logic inside of the saml server * Changed the metadata location properly * Changed the loginRequest a bit * passing a string into creating login request so that we can use redirectTo searchParams and if it comes from login directly we just use '/', added taking relaystate out of body in parseLoginResponse and destructure it in callback file. * narrowed down type of relayState to string * I forgot to save the changes last time * changed to string or default '/' for createLogin, used URL constructor in createLoginRequest * addition to env example --------- Co-authored-by: stefanaz2 <sfrunza@seneca.ca>
- Loading branch information
Showing
13 changed files
with
265 additions
and
13 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
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,79 @@ | ||
import type { ActionArgs } from '@remix-run/node'; | ||
import { createUserSession } from '~/session.server'; | ||
import { parseLoginResponse } from '~/saml.server'; | ||
import { redirect } from '@remix-run/node'; | ||
import { createUser, getUserByUsername } from '~/models/user.server'; | ||
|
||
/* This is the post route that the SAML response is bound to. It comes back as formData. | ||
We attempt to extract the SAML response into a json format that we can then use: | ||
The response from SimpleSAMLPhp | ||
{ | ||
conditions: { | ||
notBefore: '2023-02-16T17:09:02Z', | ||
notOnOrAfter: '2023-02-16T17:14:32Z' | ||
}, | ||
response: { | ||
id: '_cb60d5bbb95d8c820bbb306640ea535e9ffd6f23ca', | ||
issueInstant: '2023-02-16T17:09:32Z', | ||
destination: 'http://localhost:8080/login/callback', | ||
inResponseTo: '_4be46ad1-4c2c-42c7-ba1f-a23c31161fcd' | ||
}, | ||
audience: 'http://host.docker.internal:8080/sp', | ||
issuer: 'http://localhost:8081/simplesaml/saml2/idp/metadata.php', | ||
nameID: 'user1@myseneca.ca', | ||
sessionIndex: { | ||
authnInstant: '2023-02-16T17:09:32Z', | ||
sessionNotOnOrAfter: '2023-02-17T01:09:32Z', | ||
sessionIndex: '_6befc9246d7bd30b9e0faea72d0836cddf3ff8d556' | ||
}, | ||
attributes: { | ||
uid: '1', | ||
eduPersonAffiliation: 'group1', | ||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'user1@myseneca.ca', | ||
email: 'user1@myseneca.ca', | ||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname': 'Johannes', | ||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname': 'Kepler', | ||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'user1@myseneca.ca', | ||
sAMAccountName: 'user1', | ||
'http://schemas.microsoft.com/identity/claims/displayname': 'Johannes Kepler' | ||
} | ||
} | ||
*/ | ||
export const action = async ({ request }: ActionArgs) => { | ||
if (request.method !== 'POST') { | ||
// Request method is not post, why are you here? | ||
return redirect('/'); | ||
} | ||
|
||
const formData = await request.formData(); | ||
const body = Object.fromEntries(formData); | ||
const { samlResponse, relayState } = await parseLoginResponse(body); | ||
// Try and extract the username and see if there is an existing user by that name | ||
if (!samlResponse.attributes.sAMAccountName) { | ||
// TODO: Make this redirect to access denied page | ||
return redirect('/'); | ||
} | ||
const returnTo = relayState ? relayState : '/'; | ||
const username = samlResponse.attributes.sAMAccountName; | ||
// get or create user | ||
let user = await getUserByUsername(username); | ||
|
||
// If not create one | ||
if (!user) { | ||
user = await createUser( | ||
username, | ||
samlResponse.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'], | ||
samlResponse.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'], | ||
samlResponse.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'] | ||
); | ||
} | ||
|
||
// Either way create a session | ||
return createUserSession({ | ||
request: request, | ||
username: username, | ||
remember: false, | ||
redirectTo: returnTo, | ||
}); | ||
}; |
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,15 @@ | ||
import type { LoaderArgs } from '@remix-run/node'; | ||
import { redirect } from '@remix-run/node'; | ||
import { logout } from '~/session.server'; | ||
|
||
// Logout, destroying the session with Starchart | ||
export const loader = async ({ request }: LoaderArgs) => { | ||
const url = new URL(request.url); | ||
|
||
const SAMLResponse = url.searchParams.get('SAMLResponse'); | ||
|
||
if (SAMLResponse) { | ||
return await logout(request); | ||
} | ||
return redirect('/'); | ||
}; |
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,12 @@ | ||
import type { LoaderArgs } from '@remix-run/node'; | ||
import { metadata } from '~/saml.server'; | ||
|
||
export async function loader({ params }: LoaderArgs) { | ||
const meta = metadata(); | ||
return new Response(meta, { | ||
status: 200, | ||
headers: { | ||
'Content-Type': 'text/xml', | ||
}, | ||
}); | ||
} |
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,61 @@ | ||
// saml server based on following PR | ||
// https://github.com/remix-run/examples/pull/130/files/ec66b3060fac83eec2389eb0c96aad6d8ea4aed1#diff-02d2b71e481b2495b8a72af14f09fc28238298c7f1d19a540e37c9228985b0da | ||
import * as samlify from 'samlify'; | ||
import * as validator from '@authenio/samlify-node-xmllint'; | ||
import secrets from './lib/secrets.server'; | ||
|
||
samlify.setSchemaValidator(validator); | ||
|
||
const { SAML_IDP_METADATA } = secrets; | ||
if (!SAML_IDP_METADATA) { | ||
throw new Error('Missing SAML_IDP_METADATA secret'); | ||
} | ||
|
||
// Here we configure the service provider: https://samlify.js.org/#/sp-configuration | ||
|
||
const sp = samlify.ServiceProvider({ | ||
entityID: process.env.SAML_ENTITY_ID, | ||
nameIDFormat: ['urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'], | ||
wantAssertionsSigned: true, | ||
assertionConsumerService: [ | ||
{ | ||
Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', | ||
Location: process.env.HOSTNAME + '/login/callback', | ||
}, | ||
], | ||
singleLogoutService: [ | ||
{ | ||
Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', | ||
Location: process.env.HOSTNAME + '/logout/callback', | ||
}, | ||
], | ||
}); | ||
|
||
// Take the metadata stood up by the IDP and use it as the metadata for our IDP object | ||
const idp = samlify.IdentityProvider({ | ||
metadata: SAML_IDP_METADATA, | ||
}); | ||
|
||
export function metadata() { | ||
return sp.getMetadata(); | ||
} | ||
|
||
export async function createLoginRequest(redirectUrl: string = '/') { | ||
const { context } = sp.createLoginRequest(idp, 'redirect'); | ||
const url = new URL(context); | ||
url.searchParams.append('RelayState', redirectUrl); | ||
return url.href; | ||
} | ||
|
||
export async function createLogoutRequest(user: string) { | ||
const { context } = sp.createLogoutRequest(idp, 'redirect', { nameID: user }); | ||
return context; | ||
} | ||
|
||
export async function parseLoginResponse(body: { [k: string]: FormDataEntryValue }) { | ||
const { extract } = await sp.parseLoginResponse(idp, 'post', { | ||
body, | ||
}); | ||
const relayState = body.RelayState as string; | ||
return { samlResponse: extract, relayState }; | ||
} |
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,22 @@ | ||
<?xml version="1.0"?> | ||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="http://localhost:8081/simplesaml/saml2/idp/metadata.php"> | ||
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> | ||
<md:KeyDescriptor use="signing"> | ||
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> | ||
<ds:X509Data> | ||
<ds:X509Certificate>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==</ds:X509Certificate> | ||
</ds:X509Data> | ||
</ds:KeyInfo> | ||
</md:KeyDescriptor> | ||
<md:KeyDescriptor use="encryption"> | ||
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> | ||
<ds:X509Data> | ||
<ds:X509Certificate>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==</ds:X509Certificate> | ||
</ds:X509Data> | ||
</ds:KeyInfo> | ||
</md:KeyDescriptor> | ||
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/simplesaml/saml2/idp/SingleLogoutService.php"/> | ||
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat> | ||
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/simplesaml/saml2/idp/SSOService.php"/> | ||
</md:IDPSSODescriptor> | ||
</md:EntityDescriptor> |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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