Cleanup
This commit is contained in:
parent
c3c09856bc
commit
b9e9b53941
@ -3,3 +3,12 @@
|
|||||||
- Parse the SAML Request
|
- Parse the SAML Request
|
||||||
- Create the SAML Response
|
- Create the SAML Response
|
||||||
- Fix the certificate
|
- Fix the certificate
|
||||||
|
- Install prettify
|
||||||
|
|
||||||
|
// Start a session
|
||||||
|
// Store the RelayState in the session
|
||||||
|
// Parse the SAMLRequest
|
||||||
|
// Validate the SAMLRequest
|
||||||
|
// Create SAMLResponse
|
||||||
|
// POST the SAMLResponse to ACS URL
|
||||||
|
// Remove the RelayState from the session
|
||||||
13
lib/env.ts
13
lib/env.ts
@ -1,6 +1,9 @@
|
|||||||
const config = {
|
const appUrl = process.env.APP_URL || 'http://localhost:4000';
|
||||||
appUrl: process.env.APP_URL || 'http://localhost:4000',
|
const entityId = process.env.ENTITY_ID || 'http://saml.example.com';
|
||||||
entityId: process.env.ENTITY_ID || 'http://saml.example.com',
|
const ssoUrl = `${appUrl}/saml/sso`;
|
||||||
}
|
|
||||||
|
|
||||||
export default config;
|
export default {
|
||||||
|
appUrl,
|
||||||
|
entityId,
|
||||||
|
ssoUrl,
|
||||||
|
};
|
||||||
@ -1,13 +1,9 @@
|
|||||||
import '../styles/globals.css'
|
import '../styles/globals.css'
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
import Layout from '../components/Layout'
|
|
||||||
import 'rsuite/dist/rsuite.min.css';
|
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Component {...pageProps} />
|
||||||
<Component {...pageProps} />
|
|
||||||
</Layout>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { createCertificate, createIdPSSOUrl } from '../../../../utils';
|
|
||||||
import { IdPMetadata } from '../../../../types';
|
|
||||||
import config from '../../../../lib/env'
|
|
||||||
|
|
||||||
export default async function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse<IdPMetadata | string>
|
|
||||||
) {
|
|
||||||
|
|
||||||
switch (req.method) {
|
|
||||||
case 'GET':
|
|
||||||
return await getMetadata();
|
|
||||||
default:
|
|
||||||
return res.status(405).end(`Method ${req.method} Not Allowed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get metadata for an app
|
|
||||||
async function getMetadata() {
|
|
||||||
//const {id} = req.query;
|
|
||||||
const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d';
|
|
||||||
|
|
||||||
const metadata = {
|
|
||||||
certificate: await createCertificate(),
|
|
||||||
fingerprint: '',
|
|
||||||
sso_url: createIdPSSOUrl(appId),
|
|
||||||
entity_id: config.entityId,
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { createCertificate, createIdPMetadataXML, createIdPSSOUrl } from '../../../../utils';
|
import { createCertificate, createIdPMetadataXML } from '../../../../utils';
|
||||||
import { IdPMetadata } from '../../../../types';
|
import { IdPMetadata } from '../../../../types';
|
||||||
import stream from 'stream';
|
import stream from 'stream';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
@ -19,18 +19,16 @@ export default async function handler(
|
|||||||
return res.status(405).end(`Method ${req.method} Not Allowed`);
|
return res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download metadata for an app
|
// Download metadata
|
||||||
async function downloadMetadata() {
|
async function downloadMetadata() {
|
||||||
const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d';
|
|
||||||
|
|
||||||
const xml = await createIdPMetadataXML({
|
const xml = await createIdPMetadataXML({
|
||||||
idpEntityId: config.entityId,
|
idpEntityId: config.entityId,
|
||||||
idpSsoUrl: createIdPSSOUrl(appId),
|
idpSsoUrl: config.ssoUrl,
|
||||||
certificate: await createCertificate(),
|
certificate: await createCertificate(),
|
||||||
});
|
});
|
||||||
|
|
||||||
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=mock-saml-metadata.xml');
|
||||||
|
|
||||||
await pipeline(xml, res);
|
await pipeline(xml, res);
|
||||||
}
|
}
|
||||||
@ -1,44 +0,0 @@
|
|||||||
import prisma from '../../lib/prisma';
|
|
||||||
import { GetServerSideProps } from 'next';
|
|
||||||
import { App } from '../../types';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { IdPMetadata } from '../../types';
|
|
||||||
import React, { ChangeEvent, FormEvent, useState } from 'react';
|
|
||||||
|
|
||||||
// TODO: Remove this
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
|
|
||||||
const app = await prisma.app.findUnique({
|
|
||||||
where: {
|
|
||||||
id: params?.id,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const metadata = await axios.get('http://localhost:4000/api/apps/metadata');
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
app,
|
|
||||||
metadata: metadata.data,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const ShowApp: React.FC<{app: App, metadata: IdPMetadata}> = ({app, metadata}) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>Id: {app.id}</p>
|
|
||||||
<p>name: {app.name}</p>
|
|
||||||
<p>acs_url: {app.acs_url}</p>
|
|
||||||
<p>entity_id: {app.entity_id}</p>
|
|
||||||
|
|
||||||
<strong>Metadata</strong>
|
|
||||||
<p>sso_url: {metadata.sso_url}</p>
|
|
||||||
<p>entity_id: {metadata.entity_id}</p>
|
|
||||||
<p>certificate: {metadata.certificate}</p>
|
|
||||||
|
|
||||||
<a href="/api/apps/metadata/download" className="px-3 py-2 text-white bg-red-500 rounded">Download Metadata</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ShowApp;
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import type { NextPage } from 'next';
|
|
||||||
import { ChangeEvent, FormEvent, useState } from 'react';
|
|
||||||
import Router from 'next/router';
|
|
||||||
|
|
||||||
const Apps: NextPage = () => {
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
name: null,
|
|
||||||
acs_url: null,
|
|
||||||
entity_id: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
[e.target.name]: e.target.value.trim()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const createApp = async (e: FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const { data: app } = await axios.post('/api/apps', {
|
|
||||||
...formData
|
|
||||||
});
|
|
||||||
|
|
||||||
await Router.push(`/apps/${app.id}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<form onSubmit={createApp} className="px-8 pt-6 pb-8 mb-4 bg-white rounded shadow-md">
|
|
||||||
<div className="mb-4">
|
|
||||||
<label className="block mb-2 text-sm">
|
|
||||||
App Name
|
|
||||||
<input type="text" name="name" onChange={handleInputChange} required className="w-full px-3 py-2 border rounded" placeholder="App Name" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<label className="block mb-2 text-sm">
|
|
||||||
ACS URL
|
|
||||||
<input type="text" name="acs_url" onChange={handleInputChange} required className="w-full px-3 py-2 border rounded" placeholder="ACS URL" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<label className="block mb-2 text-sm">
|
|
||||||
Entity ID
|
|
||||||
<input type="text" name="entity_id" onChange={handleInputChange} required className="w-full px-3 py-2 border rounded" placeholder="Entity ID" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" className="px-4 py-2 text-white bg-blue-500 rounded">Create App</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Apps;
|
|
||||||
@ -1,70 +1,34 @@
|
|||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import Head from 'next/head'
|
import { GetServerSideProps } from 'next';
|
||||||
import Image from 'next/image'
|
import { IdPMetadata } from '../types'
|
||||||
import styles from '../styles/Home.module.css'
|
import config from '../lib/env';
|
||||||
|
import {createCertificate} from '../utils'
|
||||||
|
import React from 'react';
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
export const getServerSideProps: GetServerSideProps = async () => {
|
||||||
|
const metadata: IdPMetadata = {
|
||||||
|
ssoUrl: config.ssoUrl,
|
||||||
|
entityId: config.entityId,
|
||||||
|
certificate: await createCertificate(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
metadata
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Home: React.FC<{metadata: IdPMetadata}> = ({ metadata }) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div>
|
||||||
<Head>
|
<strong>Mock IdP Metadata</strong>
|
||||||
<title>Create Next App</title>
|
<p>SSO URL: {metadata.ssoUrl}</p>
|
||||||
<meta name="description" content="Generated by create next app" />
|
<p>Entity ID: {metadata.entityId}</p>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<p>Certificate: {metadata.certificate}</p>
|
||||||
</Head>
|
<br></br>
|
||||||
|
<p><Link href="/api/saml/metadata/download">Download Metadata</Link></p>
|
||||||
<main className={styles.main}>
|
|
||||||
<h1 className={styles.title}>
|
|
||||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p className={styles.description}>
|
|
||||||
Get started by editing{' '}
|
|
||||||
<code className={styles.code}>pages/index.tsx</code>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className={styles.grid}>
|
|
||||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
|
||||||
<h2>Documentation →</h2>
|
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
|
||||||
<h2>Learn →</h2>
|
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://github.com/vercel/next.js/tree/master/examples"
|
|
||||||
className={styles.card}
|
|
||||||
>
|
|
||||||
<h2>Examples →</h2>
|
|
||||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
>
|
|
||||||
<h2>Deploy →</h2>
|
|
||||||
<p>
|
|
||||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer className={styles.footer}>
|
|
||||||
<a
|
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Powered by{' '}
|
|
||||||
<span className={styles.logo}>
|
|
||||||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
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} from '../../../utils'
|
import { extractSAMLRequestAttributes } from '../../utils'
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async ({query, params}) => {
|
export const getServerSideProps: GetServerSideProps = async ({query, params}) => {
|
||||||
const relayState = query.RelayState as string;
|
const relayState = query.RelayState as string;
|
||||||
const samlRequest = query.SAMLRequest as string;
|
const samlRequest = query.SAMLRequest as string;
|
||||||
|
|
||||||
const attributes = await extractSAMLRequestAttributes(samlRequest);
|
const attributes = await extractSAMLRequestAttributes(samlRequest);
|
||||||
|
|
||||||
@ -20,19 +20,9 @@ export const getServerSideProps: GetServerSideProps = async ({query, params}) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ProcessRequest: React.FC<AuthNRequest> = ({relayState, samlRequest}) => {
|
const ProcessRequest: React.FC<AuthNRequest> = ({relayState, samlRequest}) => {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>Process Request</div>
|
<div>Process Request</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProcessRequest;
|
export default ProcessRequest;
|
||||||
|
|
||||||
// Start a session
|
|
||||||
// Store the RelayState in the session
|
|
||||||
// Parse the SAMLRequest
|
|
||||||
// Validate the SAMLRequest
|
|
||||||
// Create SAMLResponse
|
|
||||||
// POST the SAMLResponse to ACS URL
|
|
||||||
// Remove the RelayState from the session
|
|
||||||
0
pages/saml/sso.tsx
Normal file
0
pages/saml/sso.tsx
Normal file
@ -4,8 +4,8 @@ export type ServiceProvider = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type IdentityProvider = {
|
export type IdentityProvider = {
|
||||||
sso_url: string;
|
ssoUrl: string;
|
||||||
entity_id: string;
|
entityId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type App = {
|
export type App = {
|
||||||
|
|||||||
@ -76,10 +76,6 @@ const createSAMLResponseXML = async (user: User): Promise<string> => {
|
|||||||
.replace('user_lastName', 'K');
|
.replace('user_lastName', 'K');
|
||||||
};
|
};
|
||||||
|
|
||||||
const createIdPSSOUrl = (appId: string) => {
|
|
||||||
return `${config.appUrl}/saml2/apps/${appId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
parseXML,
|
parseXML,
|
||||||
extractSAMLRequestAttributes,
|
extractSAMLRequestAttributes,
|
||||||
@ -87,5 +83,4 @@ export {
|
|||||||
createSAMLResponseXML,
|
createSAMLResponseXML,
|
||||||
createCertificate,
|
createCertificate,
|
||||||
extractCert,
|
extractCert,
|
||||||
createIdPSSOUrl,
|
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user