Make "create app" UI nicer, and make IDP redirect URIs use custom domain (#3)
This commit is contained in:
parent
7f173bcabf
commit
2c14bfebc9
@ -4,7 +4,6 @@ import { kv } from "@vercel/kv";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import { App } from "@/app/app";
|
import { App } from "@/app/app";
|
||||||
import { list } from "postcss";
|
|
||||||
|
|
||||||
export async function createApp() {
|
export async function createApp() {
|
||||||
const id = `app_${ulid().toLowerCase()}`;
|
const id = `app_${ulid().toLowerCase()}`;
|
||||||
|
|||||||
@ -18,5 +18,5 @@ export function appIdpEntityId(app: App): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function appIdpRedirectUrl(app: App): string {
|
export function appIdpRedirectUrl(app: App): string {
|
||||||
return `https://${process.env.VERCEL_URL}/apps/${app.id}/sso`;
|
return `https://${process.env.DUMMYIDP_CUSTOM_DOMAIN || process.env.VERCEL_URL}/apps/${app.id}/sso`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,7 @@ export function LoginForm({
|
|||||||
|
|
||||||
setAssertion(
|
setAssertion(
|
||||||
await encodeAssertion(key, {
|
await encodeAssertion(key, {
|
||||||
|
assertionId: crypto.randomUUID(),
|
||||||
idpEntityId: appIdpEntityId(app),
|
idpEntityId: appIdpEntityId(app),
|
||||||
subjectId: user.email,
|
subjectId: user.email,
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
|
|||||||
@ -1,15 +1,25 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { createApp } from "@/app/actions";
|
import { createApp } from "@/app/actions";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { SymbolIcon } from "@radix-ui/react-icons";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
export default function CreateAppButton() {
|
export default function CreateAppButton() {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
return (
|
return (
|
||||||
<a
|
<div
|
||||||
className="inline-flex items-center justify-center px-4 py-[calc(theme(spacing.2)-1px)] rounded-full border border-transparent bg-gray-950 shadow-md whitespace-nowrap text-base font-medium text-white disabled:bg-gray-950 hover:bg-gray-800 data-disabled:opacity-40"
|
className={clsx(
|
||||||
onClick={() => createApp()}
|
"cursor-pointer inline-flex items-center justify-center px-4 py-[calc(theme(spacing.2)-1px)] rounded-full border border-transparent bg-gray-950 shadow-md whitespace-nowrap text-base font-medium text-white disabled:bg-gray-950 hover:bg-gray-800 disabled:opacity-40",
|
||||||
href="#"
|
loading && "opacity-40",
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
setLoading(true);
|
||||||
|
createApp();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Get started
|
Get started
|
||||||
</a>
|
{loading && <SymbolIcon className="ml-2 h-4 w-4 animate-spin" />}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export interface AssertionData {
|
export interface AssertionData {
|
||||||
|
assertionId: string;
|
||||||
idpEntityId: string;
|
idpEntityId: string;
|
||||||
subjectId: string;
|
subjectId: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@ -30,7 +31,7 @@ function signedAssertion(
|
|||||||
digest: string,
|
digest: string,
|
||||||
signature: string,
|
signature: string,
|
||||||
): string {
|
): string {
|
||||||
return `<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><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><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>${digest}</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>${signature}</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTExMjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJHsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDjyfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHOaDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYEFD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzmRNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6zFIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbxUUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7JQkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+jzGhYL6m9gFTm/8=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Issuer>${assertionData.idpEntityId}</saml2:Issuer><saml2:Subject><saml2:NameID>${assertionData.subjectId}</saml2:NameID><saml2:SubjectConfirmation><saml2:SubjectConfirmationData InResponseTo="${assertionData.sessionId}"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="${assertionData.now}" NotOnOrAfter="${assertionData.expire}"><saml2:AudienceRestriction><saml2:Audience>${assertionData.spEntityId}</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AttributeStatement><saml2:Attribute Name="firstName"><saml2:AttributeValue>${assertionData.firstName}</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName"><saml2:AttributeValue>${assertionData.lastName}</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>`;
|
return `<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="${assertionData.assertionId}"><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><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>${digest}</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>${signature}</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTExMjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJHsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDjyfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHOaDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYEFD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzmRNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6zFIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbxUUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7JQkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+jzGhYL6m9gFTm/8=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Issuer>${assertionData.idpEntityId}</saml2:Issuer><saml2:Subject><saml2:NameID>${assertionData.subjectId}</saml2:NameID><saml2:SubjectConfirmation><saml2:SubjectConfirmationData InResponseTo="${assertionData.sessionId}"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="${assertionData.now}" NotOnOrAfter="${assertionData.expire}"><saml2:AudienceRestriction><saml2:Audience>${assertionData.spEntityId}</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AttributeStatement><saml2:Attribute Name="firstName"><saml2:AttributeValue>${assertionData.firstName}</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName"><saml2:AttributeValue>${assertionData.lastName}</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signatureValue(
|
async function signatureValue(
|
||||||
@ -61,7 +62,7 @@ async function digestValue(assertionData: AssertionData): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function digestPart(assertionData: AssertionData): string {
|
function digestPart(assertionData: AssertionData): string {
|
||||||
return `<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer>${assertionData.idpEntityId}</saml2:Issuer><saml2:Subject><saml2:NameID>${assertionData.subjectId}</saml2:NameID><saml2:SubjectConfirmation><saml2:SubjectConfirmationData InResponseTo="${assertionData.sessionId}"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="${assertionData.now}" NotOnOrAfter="${assertionData.expire}"><saml2:AudienceRestriction><saml2:Audience>${assertionData.spEntityId}</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AttributeStatement><saml2:Attribute Name="firstName"><saml2:AttributeValue>${assertionData.firstName}</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName"><saml2:AttributeValue>${assertionData.lastName}</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>`;
|
return `<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="${assertionData.assertionId}"><saml2:Issuer>${assertionData.idpEntityId}</saml2:Issuer><saml2:Subject><saml2:NameID>${assertionData.subjectId}</saml2:NameID><saml2:SubjectConfirmation><saml2:SubjectConfirmationData InResponseTo="${assertionData.sessionId}"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="${assertionData.now}" NotOnOrAfter="${assertionData.expire}"><saml2:AudienceRestriction><saml2:Audience>${assertionData.spEntityId}</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AttributeStatement><saml2:Attribute Name="firstName"><saml2:AttributeValue>${assertionData.firstName}</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="lastName"><saml2:AttributeValue>${assertionData.lastName}</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrayBufferToBase64(buffer: ArrayBuffer): string {
|
function arrayBufferToBase64(buffer: ArrayBuffer): string {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user