diff --git a/Dockerfile b/Dockerfile index a9bbac2..31ea243 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG NODEJS_IMAGE=node:20.10.0-alpine3.18 +ARG NODEJS_IMAGE=node:20.11.0-alpine3.19 FROM --platform=$BUILDPLATFORM $NODEJS_IMAGE AS base FROM base AS deps diff --git a/README.md b/README.md index 98cb88d..1aec1be 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Mock SAML is a free SAML 2.0 Identity Provider for testing SAML SSO integrations. -Try [Mock SAML](https://mocksaml.com/), our free hosted service. +Try [Mock SAML](https://mocksaml.com/), our free hosted service. Whilst we use the root domain for our own testing you can create your own unique namespace by navigating to https://mocksaml.com/namespace/. ## Install diff --git a/lib/entity-id.ts b/lib/entity-id.ts new file mode 100644 index 0000000..57e5cee --- /dev/null +++ b/lib/entity-id.ts @@ -0,0 +1,5 @@ +const getEntityId = (entityId: string, namespace: string | undefined) => { + return namespace ? `${entityId}/${namespace}` : entityId; +}; + +export { getEntityId }; diff --git a/pages/api/namespace/[namespace]/saml/auth.ts b/pages/api/namespace/[namespace]/saml/auth.ts new file mode 100644 index 0000000..41350ed --- /dev/null +++ b/pages/api/namespace/[namespace]/saml/auth.ts @@ -0,0 +1,3 @@ +import handler from 'pages/api/saml/auth'; + +export default handler; diff --git a/pages/api/namespace/[namespace]/saml/metadata.ts b/pages/api/namespace/[namespace]/saml/metadata.ts new file mode 100644 index 0000000..67ede07 --- /dev/null +++ b/pages/api/namespace/[namespace]/saml/metadata.ts @@ -0,0 +1,3 @@ +import handler from 'pages/api/saml/metadata'; + +export default handler; diff --git a/pages/api/saml/auth.ts b/pages/api/saml/auth.ts index 8b92cf2..fa3accf 100644 --- a/pages/api/saml/auth.ts +++ b/pages/api/saml/auth.ts @@ -4,6 +4,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import type { User } from 'types'; import { createResponseXML, signResponseXML } from 'utils'; import saml from '@boxyhq/saml20'; +import { getEntityId } from 'lib/entity-id'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === 'POST') { @@ -24,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }; const xml = await createResponseXML({ - idpIdentityId: config.entityId, + idpIdentityId: getEntityId(config.entityId, req.query.namespace as any), audience, acsUrl, samlReqId: id, diff --git a/pages/api/saml/metadata/index.ts b/pages/api/saml/metadata.ts similarity index 76% rename from pages/api/saml/metadata/index.ts rename to pages/api/saml/metadata.ts index 045b1c0..40bb8c3 100644 --- a/pages/api/saml/metadata/index.ts +++ b/pages/api/saml/metadata.ts @@ -6,6 +6,7 @@ import type { IdPMetadata } from 'types'; import { createIdPMetadataXML } from 'utils'; import stream from 'stream'; import { promisify } from 'util'; +import { getEntityId } from 'lib/entity-id'; const pipeline = promisify(stream.pipeline); @@ -21,8 +22,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< async function MetadataUrl() { const { download } = req.query as { download: any }; + const filename = 'mock-saml-metadata' + (req.query.namespace ? `-${req.query.namespace}` : '') + '.xml'; + const xml = await createIdPMetadataXML({ - idpEntityId: config.entityId, + idpEntityId: getEntityId(config.entityId, req.query.namespace as any), idpSsoUrl: config.ssoUrl, certificate: saml.stripCertHeaderAndFooter(config.publicKey), }); @@ -30,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< res.setHeader('Content-type', 'text/xml'); if (download || download === '') { - res.setHeader('Content-Disposition', 'attachment; filename=mock-saml-metadata.xml'); + res.setHeader('Content-Disposition', `attachment; filename=${filename}`); await pipeline(xml, res); return; diff --git a/pages/index.tsx b/pages/index.tsx index 710c125..63f3f3f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -3,19 +3,25 @@ import Link from 'next/link'; import React from 'react'; import config from '../lib/env'; import { IdPMetadata } from '../types'; +import { getEntityId } from 'lib/entity-id'; + +const Home: React.FC<{ metadata: IdPMetadata; params: any }> = ({ metadata, params }) => { + const namespace = params.namespace; -const Home: React.FC<{ metadata: IdPMetadata }> = ({ metadata }) => { const { ssoUrl, entityId, certificate } = metadata; - + const namespaceEntityId = getEntityId(entityId, namespace); + const metadataDownloadUrl = + '/api' + (namespace ? `/namespace/${namespace}` : '') + '/saml/metadata?download=true'; + const metadataUrl = '/api' + (namespace ? `/namespace/${namespace}` : '') + '/saml/metadata'; return ( -
+

A free SAML 2.0 Identity Provider for testing SAML SSO integrations.

- + = ({ metadata }) => { Download Metadata - + Metadata URL
@@ -52,7 +58,7 @@ const Home: React.FC<{ metadata: IdPMetadata }> = ({ metadata }) => { - +