Create saml response
This commit is contained in:
parent
7c739065bf
commit
aaa007f005
@ -12,3 +12,5 @@
|
|||||||
// Create SAMLResponse
|
// Create SAMLResponse
|
||||||
// POST the SAMLResponse to ACS URL
|
// POST the SAMLResponse to ACS URL
|
||||||
// Remove the RelayState from the session
|
// Remove the RelayState from the session
|
||||||
|
|
||||||
|
https://localhost:4000/api/saml/sso?RelayState=boxyhq_jackson_baf6be45ae7f5fe53a005718f941a13e&SAMLRequest=nVXRkqI4FP0VCx8thURbhVKnsOlubVFR0VZftiIEiB0SJUHUrx%2FUtdetnZmd3bdUOPecc0%2BSS%2BvbMaaFA04E4aytgIqmfOu0BIrpzjBTGbEp3qdYyEIOY8K4fmgracIMjgQRBkMxFob0jJk5tA1Y0YxdwiX3OFUKfaut%2FLHRNQ%2FAQNObgY70Rs3HzYZSWNwF84ocKESK%2B0xIxGS%2BpUFY1mAZAlerGVrVAPVKE4K1UnD%2BpO4S5hMW%2FtrH5gYSRs91nbIznrlKwRQCJzIXfuZMpDFOZjg5EA%2FPp3ZbiaTcGapKuYdoxIU0qpqmqUJwFXniKn4gPk5GuVJb6fLjqTdRblEZ1waSh4x%2BbQ3dbSidi6jIVS9FlU1OGu0rHo9b6gNvpzUjIUMyTfBN4strlmWVrFrhSajCi1lNV3OAL0hYVG5V2O%2BzgHdaz4hxRvLOyBldlIdYRtwvmDTkCZFR%2FBNKoALtQlnGR6%2FsgRorKuqDnd9m0Wp3Y%2BWYJ7iYCFQWEYJP9QvfFAc4wczDhfm031aKP740nZabICYCnsTiYf2v6n%2BLBbMDpnyH%2FbK4N3Fx8PtsP05EfbRmkTB%2FMf8xm7z94l%2BJ3CgWiKa4s4sb%2FZW7%2BPRDOmTvq0VvuY4%2B6TsdkE3Y3NZWAZx61mgfx4tJu6U%2BVrbUr2Tz9eN1%2BDrBGzB4eZuHcaS%2FTO0F%2BISzTCxHtWA4Wa8ndu%2FsepFlUmibNZsKxx9szvpHyvzNfPZiisnYXgMYIf%2Ftie7GDgh7Vhb4cUitLQAyhQtg7atHKdwqXPTTM5zr9cFczeqr8WZXGwBzt946%2B%2FnMtdbAs7xDSaOwWn19ZyF00Wl2BNECqUkwxdHi4DDW%2F%2BADa1PC20kXhvOoXtvLaB4dtabbJVa1kYk9eV27p9VJP0U20MzD%2BEjny9IbOdmWP172MR090W6zC%2B3JMOrCrK%2FbQ1y1PrYlFWSzl7HVtKZhdABxF%2BQ57fkSxKXhp3w%2B78mIjEofvV5tGTUHOy1rOtsAkbS7jpf2myO9szgfwdJtqOe0NCNHB1TNdvsW%2BkPQDxv3KXuZJ33L4ZR4p%2F8zZV%2FzS4fkz9GgAq47xC8HV6iBY0So6fsJFvlcMynl2XOCkczHmkzS63tQ%2F%2FkH6HwH
|
||||||
@ -1,6 +1,6 @@
|
|||||||
const appUrl = process.env.APP_URL || 'http://localhost:4000';
|
const appUrl = process.env.APP_URL || 'http://localhost:4000';
|
||||||
const entityId = process.env.ENTITY_ID || 'http://saml.example.com';
|
const entityId = process.env.ENTITY_ID || 'http://saml.example.com';
|
||||||
const ssoUrl = `${appUrl}/saml/sso`;
|
const ssoUrl = `${appUrl}/api/saml/sso`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
appUrl,
|
appUrl,
|
||||||
|
|||||||
48
pages/api/saml/sso.ts
Normal file
48
pages/api/saml/sso.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { createResponseForm, createSAMLResponseXML } from 'utils';
|
||||||
|
import { User } from 'types';
|
||||||
|
import config from '../../../lib/env'
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<string>
|
||||||
|
) {
|
||||||
|
|
||||||
|
switch (req.method) {
|
||||||
|
case 'GET':
|
||||||
|
return await processSAMLRequest();
|
||||||
|
default:
|
||||||
|
return res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processSAMLRequest() {
|
||||||
|
const relayState = <string>req.query.RelayState;
|
||||||
|
const samlRequest = <string>req.query.SAMLRequest;
|
||||||
|
|
||||||
|
const idpIdentityId = config.entityId;
|
||||||
|
const audience = config.entityId;
|
||||||
|
const acsUrl = 'http://localhost:3000/sso/acs'; // TODO: Fetch acsUrl from SAMLRequest
|
||||||
|
|
||||||
|
const user: User = {
|
||||||
|
id: '1',
|
||||||
|
email: 'kiran@boxyhq.com',
|
||||||
|
firstName: 'Kiran',
|
||||||
|
lastName: 'K',
|
||||||
|
};
|
||||||
|
|
||||||
|
const xml = await createSAMLResponseXML({
|
||||||
|
idpIdentityId: idpIdentityId,
|
||||||
|
audience: audience,
|
||||||
|
acsUrl: acsUrl,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(xml)
|
||||||
|
|
||||||
|
const encodedSamlResponse = Buffer.from(xml).toString('base64');
|
||||||
|
|
||||||
|
const html = createResponseForm(relayState, encodedSamlResponse, acsUrl);
|
||||||
|
|
||||||
|
res.send(html);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,23 +1,7 @@
|
|||||||
import type { GetServerSideProps } from 'next';
|
import type { GetServerSideProps } from 'next';
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { AuthNRequest } from '../../types'
|
import { AuthNRequest } from '../../types'
|
||||||
import { extractSAMLRequestAttributes, createSAMLResponse } from '../../utils'
|
import { extractSAMLRequestAttributes, createResponseForm } from '../../utils'
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({query, params}) => {
|
|
||||||
const relayState = query.RelayState as string;
|
|
||||||
const samlRequest = query.SAMLRequest as string;
|
|
||||||
|
|
||||||
console.log(await createSAMLResponse())
|
|
||||||
|
|
||||||
const attributes = await extractSAMLRequestAttributes(samlRequest);
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
relayState,
|
|
||||||
samlRequest,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProcessRequest: React.FC<AuthNRequest> = ({relayState, samlRequest}) => {
|
const ProcessRequest: React.FC<AuthNRequest> = ({relayState, samlRequest}) => {
|
||||||
return (
|
return (
|
||||||
@ -26,3 +10,4 @@ const ProcessRequest: React.FC<AuthNRequest> = ({relayState, samlRequest}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ProcessRequest;
|
export default ProcessRequest;
|
||||||
|
|
||||||
|
|||||||
@ -67,18 +67,13 @@ const extractCert = (certificate: string) => {
|
|||||||
.trim();
|
.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create SAMLResponse
|
const createSAMLResponseXML = async (params: {
|
||||||
const createSAMLResponse = async (): Promise<string> => {
|
idpIdentityId: string,
|
||||||
const idpIdentityId = 'urn:dev-tyj7qyzz.auth0.com';
|
audience: string,
|
||||||
const audience = 'https://saml.boxyhq.com';
|
acsUrl: string,
|
||||||
const acsUrl = 'http://localhost:3000/sso/acs';
|
user: User
|
||||||
|
}): Promise<string> => {
|
||||||
const user: User = {
|
const {idpIdentityId, audience, acsUrl, user} = params;
|
||||||
id: '1',
|
|
||||||
email: 'kiran@boxyhq.com',
|
|
||||||
firstName: 'Kiran',
|
|
||||||
lastName: 'K',
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodes = {
|
const nodes = {
|
||||||
'samlp:Response':{
|
'samlp:Response':{
|
||||||
@ -136,24 +131,36 @@ const createSAMLResponse = async (): Promise<string> => {
|
|||||||
'saml:AttributeStatement': {
|
'saml:AttributeStatement': {
|
||||||
'@xmlns:xs': 'http://www.w3.org/2001/XMLSchema',
|
'@xmlns:xs': 'http://www.w3.org/2001/XMLSchema',
|
||||||
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
'saml:Attribute': {
|
'saml:Attribute': [
|
||||||
'@Name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
{
|
||||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
|
'@Name': 'id',
|
||||||
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
|
'saml:AttributeValue': {
|
||||||
|
'#text': user.id,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'@Name': 'email',
|
||||||
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
'saml:AttributeValue': {
|
'saml:AttributeValue': {
|
||||||
'@xsi:type': 'xs:string',
|
|
||||||
'#text': user.email,
|
'#text': user.email,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
// @ts-ignore
|
'@Name': 'firstName',
|
||||||
'saml:Attribute': {
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
'@Name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
|
||||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
|
|
||||||
'saml:AttributeValue': {
|
'saml:AttributeValue': {
|
||||||
'@xsi:type': 'xs:string',
|
'#text': user.firstName,
|
||||||
'#text': user.id
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'@Name': 'lastName',
|
||||||
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
|
'saml:AttributeValue': {
|
||||||
|
'#text': user.lastName,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +169,8 @@ const createSAMLResponse = async (): Promise<string> => {
|
|||||||
return xmlbuilder.create(nodes).end({ pretty: true});
|
return xmlbuilder.create(nodes).end({ pretty: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createResponseForm = (relayState: string, samlResponse: string, acsUrl: string) => {
|
// Create the HTML form to submit the response
|
||||||
|
export const createResponseForm = (relayState: string, encodedSamlResponse: string, acsUrl: string) => {
|
||||||
const formElements = [
|
const formElements = [
|
||||||
'<!DOCTYPE html>',
|
'<!DOCTYPE html>',
|
||||||
'<html>',
|
'<html>',
|
||||||
@ -176,7 +184,7 @@ export const createResponseForm = (relayState: string, samlResponse: string, acs
|
|||||||
'</noscript>',
|
'</noscript>',
|
||||||
'<form method="post" action="' + encodeURI(acsUrl) + '">',
|
'<form method="post" action="' + encodeURI(acsUrl) + '">',
|
||||||
'<input type="hidden" name="RelayState" value="' + relayState + '"/>',
|
'<input type="hidden" name="RelayState" value="' + relayState + '"/>',
|
||||||
'<input type="hidden" name="SAMLResponse" value="' + samlResponse + '"/>',
|
'<input type="hidden" name="SAMLResponse" value="' + encodedSamlResponse + '"/>',
|
||||||
'<input type="submit" value="Continue" />',
|
'<input type="submit" value="Continue" />',
|
||||||
'</form>',
|
'</form>',
|
||||||
'<script>document.forms[0].style.display="none";</script>',
|
'<script>document.forms[0].style.display="none";</script>',
|
||||||
@ -191,7 +199,7 @@ export {
|
|||||||
parseXML,
|
parseXML,
|
||||||
extractSAMLRequestAttributes,
|
extractSAMLRequestAttributes,
|
||||||
createIdPMetadataXML,
|
createIdPMetadataXML,
|
||||||
createSAMLResponse,
|
createSAMLResponseXML,
|
||||||
createCertificate,
|
createCertificate,
|
||||||
extractCert,
|
extractCert,
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user