wip
This commit is contained in:
parent
505416e828
commit
141a920a50
@ -7,4 +7,4 @@
|
|||||||
- Fix the SAML metadata URL
|
- Fix the SAML metadata URL
|
||||||
- Improve the UI
|
- 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 type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import path from 'path';
|
import { createCertificate, createIdPMetadataXML } from '../../../utils';
|
||||||
import { metadata } from '../../../services';
|
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
@ -12,12 +10,17 @@ export default async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function download(req: NextApiRequest) {
|
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 createCertificate();
|
||||||
const certificate = await fs.readFile(certificateFilePath, 'utf8');
|
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-type', 'text/xml');
|
||||||
res.setHeader('Content-Disposition', 'attachment; filename="metadata.xml"');
|
res.setHeader('Content-Disposition', 'attachment; filename="metadata.xml"');
|
||||||
|
|||||||
@ -1,39 +1,33 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import xml2js from 'xml2js';
|
import { User } from '../../../types';
|
||||||
|
import {
|
||||||
const parseXML = (xml: string): Promise<Record<string, any>> => {
|
createSAMLResponseXML,
|
||||||
return new Promise((resolve, reject) => {
|
extractSAMLRequestAttributes,
|
||||||
xml2js.parseString(xml, (err: Error, result: any) => {
|
} from '../../../utils';
|
||||||
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'],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse<any>
|
res: NextApiResponse<any>
|
||||||
) {
|
) {
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'POST') {
|
||||||
return await response(req);
|
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) {
|
async function response(req: NextApiRequest) {
|
||||||
const { RelayState, SAMLRequest } = req.query;
|
const { RelayState, SAMLRequest } = req.query;
|
||||||
|
|
||||||
const attributes = await extractSAMLRequestAttribute(SAMLRequest);
|
const attributes = await extractSAMLRequestAttributes(SAMLRequest);
|
||||||
|
|
||||||
return res.status(200).json(attributes);
|
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';
|
import type { IdPMetadata } from '../types';
|
||||||
|
|
||||||
const baseUrl = 'http://localhost:3000/saml';
|
const baseUrl = 'http://localhost:3000/saml';
|
||||||
@ -19,50 +18,3 @@ export const create = (
|
|||||||
certificate: certificate,
|
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;
|
RelayState: string;
|
||||||
SAMLRequest: SAMLRequest;
|
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