wip
This commit is contained in:
parent
505416e828
commit
141a920a50
@ -7,4 +7,4 @@
|
||||
- Fix the SAML metadata URL
|
||||
- Improve the UI
|
||||
|
||||
http://localhost:4000/api/apps/saml?RelayState=boxyhq_jackson_2fd72712996df6104811ff8cc233d1e2&SAMLRequest=PD94bWwgdmVyc2lvbj0iMS4wIj8%2BPHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iXzZiMmU1ZDdhMDRiNjEzMzAyZDhmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMi0wMS0xNFQxNToxODoxNi4yNTlaIiBQcm90b2NvbEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW5nczpIVFRQLVBPU1QiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly8yOGEyLTEwMy0xNTMtMTA0LTQzLm5ncm9rLmlvL3Nzby9hY3MiIFByb3ZpZGVyTmFtZT0iQm94eUhRIj48c2FtbDpJc3N1ZXIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI%2BaHR0cHM6Ly9zYW1sLmJveHlocS5jb208L3NhbWw6SXNzdWVyPjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8%2BPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI182YjJlNWQ3YTA0YjYxMzMwMmQ4ZiI%2BPFRyYW5zZm9ybXM%2BPFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BPFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvVHJhbnNmb3Jtcz48RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPERpZ2VzdFZhbHVlPlBXVFlGek9hV1FvZHNqK21haEdnam1VQkZsVFk5cGlQNy8yVVkvNEZkN289PC9EaWdlc3RWYWx1ZT48L1JlZmVyZW5jZT48L1NpZ25lZEluZm8%2BPFNpZ25hdHVyZVZhbHVlPm5HbEMrZVkwMC82eDZQNlBMcGFoOVR5QzlRTnNKR0U3V0Y1czQ1SXZRbWlJWmwrSmhNVGRQeExBSjJ1dVpQMWZzZ1NwUllPZG5HanRLNjlobS9LOHZDVGlab29EODdjdkJmdXp4NVVNQ3NiV0VQSERJV01rV1k3S2ZrYk5ySjNMdVZyYTR0SEZvY2luQnVwRzNMeVQ5dUtwRzI1NlQrNm9LUDNOdkJSTzRROUFpWlk4czlxaVhzRE9QQlJRd3NEaEdNdGRJcGVDaGZQU2RQdFV1RXV3UEdRS1pZellFY0d3WHpOTGYydm5PaS9QUE5rRHA4QTdPN2FJd09HWFNDVVlqL29oNUo4RWRQdVpyRzFuL0ZMdm04L0dNY2pNTTYwQWZ1NFg3WGMzRlFRUG5aZ2lxdFlsd2YwbjFDemlkbnYvbDE3NjF6T3grWXJqb08zaUp0SHB6Zz09PC9TaWduYXR1cmVWYWx1ZT48L1NpZ25hdHVyZT48c2FtbHA6TmFtZUlEUG9saWN5IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D
|
||||
http://localhost:4000/apps/saml?RelayState=boxyhq_jackson_2fd72712996df6104811ff8cc233d1e2&SAMLRequest=PD94bWwgdmVyc2lvbj0iMS4wIj8%2BPHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iXzZiMmU1ZDdhMDRiNjEzMzAyZDhmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMi0wMS0xNFQxNToxODoxNi4yNTlaIiBQcm90b2NvbEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW5nczpIVFRQLVBPU1QiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly8yOGEyLTEwMy0xNTMtMTA0LTQzLm5ncm9rLmlvL3Nzby9hY3MiIFByb3ZpZGVyTmFtZT0iQm94eUhRIj48c2FtbDpJc3N1ZXIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI%2BaHR0cHM6Ly9zYW1sLmJveHlocS5jb208L3NhbWw6SXNzdWVyPjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8%2BPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI182YjJlNWQ3YTA0YjYxMzMwMmQ4ZiI%2BPFRyYW5zZm9ybXM%2BPFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BPFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvVHJhbnNmb3Jtcz48RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPERpZ2VzdFZhbHVlPlBXVFlGek9hV1FvZHNqK21haEdnam1VQkZsVFk5cGlQNy8yVVkvNEZkN289PC9EaWdlc3RWYWx1ZT48L1JlZmVyZW5jZT48L1NpZ25lZEluZm8%2BPFNpZ25hdHVyZVZhbHVlPm5HbEMrZVkwMC82eDZQNlBMcGFoOVR5QzlRTnNKR0U3V0Y1czQ1SXZRbWlJWmwrSmhNVGRQeExBSjJ1dVpQMWZzZ1NwUllPZG5HanRLNjlobS9LOHZDVGlab29EODdjdkJmdXp4NVVNQ3NiV0VQSERJV01rV1k3S2ZrYk5ySjNMdVZyYTR0SEZvY2luQnVwRzNMeVQ5dUtwRzI1NlQrNm9LUDNOdkJSTzRROUFpWlk4czlxaVhzRE9QQlJRd3NEaEdNdGRJcGVDaGZQU2RQdFV1RXV3UEdRS1pZellFY0d3WHpOTGYydm5PaS9QUE5rRHA4QTdPN2FJd09HWFNDVVlqL29oNUo4RWRQdVpyRzFuL0ZMdm04L0dNY2pNTTYwQWZ1NFg3WGMzRlFRUG5aZ2lxdFlsd2YwbjFDemlkbnYvbDE3NjF6T3grWXJqb08zaUp0SHB6Zz09PC9TaWduYXR1cmVWYWx1ZT48L1NpZ25hdHVyZT48c2FtbHA6TmFtZUlEUG9saWN5IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D
|
||||
|
||||
15
data/idp-metadata.xml
Normal file
15
data/idp-metadata.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="idp_entity_id" validUntil="2026-06-22T18:39:53.000Z">
|
||||
<md:IDPSSODescriptor WantAuthnRequestsSigned="false" 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>idp_certificate</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
|
||||
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="idp_sso_url"/>
|
||||
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="idp_sso_url"/>
|
||||
</md:IDPSSODescriptor>
|
||||
</md:EntityDescriptor>
|
||||
58
data/saml-response.xml
Normal file
58
data/saml-response.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://28a2-103-153-104-43.ngrok.io/sso/acs" ID="_41a01ff26e2669a4ffe974cd57079b3c" InResponseTo="_f5da12eb6e20fd04b654" IssueInstant="2022-01-14T18:39:01.757Z" Version="2.0">
|
||||
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">idp_entity_id</saml2:Issuer>
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:SignedInfo>
|
||||
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
||||
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
||||
<ds:Reference URI="#_41a01ff26e2669a4ffe974cd57079b3c">
|
||||
<ds:Transforms>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
||||
</ds:Transforms>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>xPMc7SSHhFSWGljyk1L8mRE1M6otu0qlukR42E6QdiQ=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
</ds:SignedInfo>
|
||||
<ds:SignatureValue>VXlnv1pN3BsHvfbwkugYfsgcjoiXzsbm8OiWczkbNQcadaYTHgIOPf9mNLLJXq1vSfBVLyDY+1Xq</ds:SignatureValue>
|
||||
<ds:KeyInfo>
|
||||
<ds:X509Data>
|
||||
<ds:X509SubjectName>ST=California,C=US,OU=Google For Work,CN=Google,L=Mountain View,O=Google Inc.</ds:X509SubjectName>
|
||||
<ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAXo6K+u/MA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</ds:Signature>
|
||||
<saml2p:Status>
|
||||
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
|
||||
</saml2p:Status>
|
||||
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_28c7bb0ebe6359c70c1b2a9b7b1f280a" IssueInstant="2022-01-14T18:39:01.757Z" Version="2.0">
|
||||
<saml2:Issuer>idp_entity_id</saml2:Issuer>
|
||||
<saml2:Subject>
|
||||
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">user_email</saml2:NameID>
|
||||
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
|
||||
<saml2:SubjectConfirmationData InResponseTo="_f5da12eb6e20fd04b654" NotOnOrAfter="2022-01-14T18:44:01.757Z" Recipient="sp_acs_url"/>
|
||||
</saml2:SubjectConfirmation>
|
||||
</saml2:Subject>
|
||||
<saml2:Conditions NotBefore="2022-01-14T18:34:01.757Z" NotOnOrAfter="2022-01-14T18:44:01.757Z">
|
||||
<saml2:AudienceRestriction>
|
||||
<saml2:Audience>https://saml.boxyhq.com</saml2:Audience>
|
||||
</saml2:AudienceRestriction>
|
||||
</saml2:Conditions>
|
||||
<saml2:AttributeStatement>
|
||||
<saml2:Attribute Name="email">
|
||||
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">user_email</saml2:AttributeValue>
|
||||
</saml2:Attribute>
|
||||
<saml2:Attribute Name="firstName">
|
||||
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">user_firstName</saml2:AttributeValue>
|
||||
</saml2:Attribute>
|
||||
<saml2:Attribute Name="lastName">
|
||||
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">user_lastName</saml2:AttributeValue>
|
||||
</saml2:Attribute>
|
||||
</saml2:AttributeStatement>
|
||||
<saml2:AuthnStatement AuthnInstant="2022-01-14T15:01:16.000Z" SessionIndex="_28c7bb0ebe6359c70c1b2a9b7b1f280a">
|
||||
<saml2:AuthnContext>
|
||||
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
|
||||
</saml2:AuthnContext>
|
||||
</saml2:AuthnStatement>
|
||||
</saml2:Assertion>
|
||||
</saml2p:Response>
|
||||
@ -1,7 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import path from 'path';
|
||||
import { metadata } from '../../../services';
|
||||
import { createCertificate, createIdPMetadataXML } from '../../../utils';
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
@ -12,12 +10,17 @@ export default async function handler(
|
||||
}
|
||||
|
||||
async function download(req: NextApiRequest) {
|
||||
const { acs_url, entity_id } = req.body;
|
||||
const { acs_url, sp_entity_id } = req.body;
|
||||
|
||||
const certificateFilePath = path.join('data', 'x509cert.txt');
|
||||
const certificate = await fs.readFile(certificateFilePath, 'utf8');
|
||||
const certificate = await createCertificate();
|
||||
const idpEntityId = 'http://localhost:4000/sso';
|
||||
const idpSsoUrl = 'http://localhost:4000/sso';
|
||||
|
||||
const xml = await metadata.createXML(acs_url, entity_id, certificate);
|
||||
const xml = await createIdPMetadataXML({
|
||||
idpEntityId,
|
||||
idpSsoUrl,
|
||||
certificate,
|
||||
});
|
||||
|
||||
res.setHeader('Content-type', 'text/xml');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="metadata.xml"');
|
||||
|
||||
@ -1,39 +1,33 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import xml2js from 'xml2js';
|
||||
|
||||
const parseXML = (xml: string): Promise<Record<string, any>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
xml2js.parseString(xml, (err: Error, result: any) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const extractSAMLRequestAttribute = async (SAMLRequest: string | string[]) => {
|
||||
// @ts-ignore
|
||||
const result = await parseXML(Buffer.from(SAMLRequest, 'base64').toString());
|
||||
const sp = result['samlp:AuthnRequest']['$'];
|
||||
|
||||
return {
|
||||
ID: sp['ID'],
|
||||
IssueInstant: sp['IssueInstant'],
|
||||
AssertionConsumerServiceURL: sp['AssertionConsumerServiceURL'],
|
||||
ProviderName: sp['ProviderName'],
|
||||
};
|
||||
};
|
||||
import { User } from '../../../types';
|
||||
import {
|
||||
createSAMLResponseXML,
|
||||
extractSAMLRequestAttributes,
|
||||
} from '../../../utils';
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<any>
|
||||
) {
|
||||
if (req.method === 'GET') {
|
||||
if (req.method === 'POST') {
|
||||
return await response(req);
|
||||
}
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const user: User = {
|
||||
id: '1',
|
||||
email: 'kiran@demo.com',
|
||||
firstName: 'Kiran',
|
||||
lastName: 'K',
|
||||
};
|
||||
|
||||
return res.status(200).json(await createSAMLResponseXML(user));
|
||||
}
|
||||
|
||||
async function response(req: NextApiRequest) {
|
||||
const { RelayState, SAMLRequest } = req.query;
|
||||
|
||||
const attributes = await extractSAMLRequestAttribute(SAMLRequest);
|
||||
const attributes = await extractSAMLRequestAttributes(SAMLRequest);
|
||||
|
||||
return res.status(200).json(attributes);
|
||||
}
|
||||
|
||||
42
pages/apps/saml.tsx
Normal file
42
pages/apps/saml.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import type { NextPage } from 'next';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
const {RelayState, SAMLRequest} = context.query;
|
||||
|
||||
return {
|
||||
props: {
|
||||
RelayState: RelayState,
|
||||
SAMLRequest: SAMLRequest
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// const createSAMLResponse = ({RelayState, SAMLRequest}: Prop) => {
|
||||
// const url = new URL('http://28a2-103-153-104-43.ngrok.io/sso/acs');
|
||||
|
||||
// url.searchParams.append('RelayState', RelayState);
|
||||
// url.searchParams.append('SAMLResponse', 'SAMLResponse');
|
||||
|
||||
// return url.href;
|
||||
// }
|
||||
|
||||
const SAML: NextPage = (prop) => {
|
||||
const [] = useState();
|
||||
const formRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
formRef?.current?.submit();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form action="http://28a2-103-153-104-43.ngrok.io/sso/acs" method="POST" ref={formRef}>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SAML;
|
||||
@ -1,4 +1,3 @@
|
||||
import * as xmlbuilder from 'xmlbuilder';
|
||||
import type { IdPMetadata } from '../types';
|
||||
|
||||
const baseUrl = 'http://localhost:3000/saml';
|
||||
@ -19,50 +18,3 @@ export const create = (
|
||||
certificate: certificate,
|
||||
};
|
||||
};
|
||||
|
||||
const extractCert = (certificate: string) => {
|
||||
return certificate
|
||||
.replace('-----BEGIN CERTIFICATE-----', '')
|
||||
.replace('-----END CERTIFICATE-----', '')
|
||||
.trim();
|
||||
};
|
||||
|
||||
export const createXML = async (
|
||||
acs_url: string,
|
||||
entity_id: string,
|
||||
certificate: string
|
||||
) => {
|
||||
const metadata = create(acs_url, entity_id, certificate);
|
||||
|
||||
const data = {
|
||||
'md:EntityDescriptor': {
|
||||
'@xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata',
|
||||
'@entityID': `${metadata.entity_id}`,
|
||||
'@validUntil': '2026-06-22T18:39:53.000Z',
|
||||
'md:IDPSSODescriptor': {
|
||||
'@WantAuthnRequestsSigned': 'false',
|
||||
'@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': {
|
||||
'#text': `${extractCert(certificate)}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'md:NameIDFormat': {
|
||||
'#text': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||
},
|
||||
'md:SingleSignOnService': {
|
||||
'@Binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
'@Location': `${metadata.sso_url}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return xmlbuilder.create(data).end({ pretty: true });
|
||||
};
|
||||
|
||||
@ -30,3 +30,10 @@ export type AuthNRequest = {
|
||||
RelayState: string;
|
||||
SAMLRequest: SAMLRequest;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
};
|
||||
|
||||
84
utils/index.ts
Normal file
84
utils/index.ts
Normal file
@ -0,0 +1,84 @@
|
||||
// @ts-ignore
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import xml2js from 'xml2js';
|
||||
import { User } from '../types';
|
||||
|
||||
// Parse XML
|
||||
const parseXML = (xml: string): Promise<Record<string, any>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
xml2js.parseString(xml, (err: Error, result: any) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Extract SAML Request Attributes
|
||||
const extractSAMLRequestAttributes = async (SAMLRequest: string | string[]) => {
|
||||
// @ts-ignore
|
||||
const result = await parseXML(Buffer.from(SAMLRequest, 'base64').toString());
|
||||
const attributes = result['samlp:AuthnRequest']['$'];
|
||||
|
||||
return {
|
||||
ID: attributes['ID'],
|
||||
IssueInstant: attributes['IssueInstant'],
|
||||
AssertionConsumerServiceURL: attributes['AssertionConsumerServiceURL'],
|
||||
ProviderName: attributes['ProviderName'],
|
||||
};
|
||||
};
|
||||
|
||||
const createIdPMetadataXML = async ({
|
||||
idpEntityId,
|
||||
idpSsoUrl,
|
||||
certificate,
|
||||
}: {
|
||||
idpEntityId: string;
|
||||
idpSsoUrl: string;
|
||||
certificate: string;
|
||||
}): Promise<string> => {
|
||||
const xmlPath = path.join('data', 'idp-metadata.xml');
|
||||
const xml = await fs.readFile(xmlPath, 'utf8');
|
||||
|
||||
return xml
|
||||
.replace('idp_entity_id', idpEntityId)
|
||||
.replace('idp_certificate', extractCert(certificate))
|
||||
.replace(/idp_sso_url/g, idpSsoUrl);
|
||||
};
|
||||
|
||||
const createCertificate = async () => {
|
||||
const certificateFilePath = path.join('data', 'x509cert.txt');
|
||||
|
||||
return await fs.readFile(certificateFilePath, 'utf8');
|
||||
};
|
||||
|
||||
const extractCert = (certificate: string) => {
|
||||
return certificate
|
||||
.replace('-----BEGIN CERTIFICATE-----', '')
|
||||
.replace('-----END CERTIFICATE-----', '')
|
||||
.trim();
|
||||
};
|
||||
|
||||
// Create SAML Response XML
|
||||
const createSAMLResponseXML = async (user: User): Promise<string> => {
|
||||
const xmlPath = path.join('data', 'saml-response.xml');
|
||||
const xml = await fs.readFile(xmlPath, 'utf8');
|
||||
|
||||
return xml
|
||||
.replace(
|
||||
/idp_entity_id/g,
|
||||
'https://accounts.google.com/o/saml2?idpid=C02frd9s1'
|
||||
)
|
||||
.replace('sp_acs_url', 'some-url')
|
||||
.replace(/user_email/g, 'kiran@demo.com')
|
||||
.replace('user_firstName', 'Kiran')
|
||||
.replace('user_lastName', 'K');
|
||||
};
|
||||
|
||||
export {
|
||||
parseXML,
|
||||
extractSAMLRequestAttributes,
|
||||
createIdPMetadataXML,
|
||||
createSAMLResponseXML,
|
||||
createCertificate,
|
||||
extractCert,
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user