Skip to content

Commit

Permalink
feat(oauth-provider): allow customizing branding of the login page
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieusieben committed Mar 8, 2024
1 parent 2c1cd32 commit 96985fe
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 134 deletions.
1 change: 0 additions & 1 deletion packages/oauth-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"postcss": "^8.4.33",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.13",
"rollup": "^4.10.0",
"rollup-plugin-postcss": "^4.0.2",
"tailwindcss": "^3.4.1",
Expand Down
24 changes: 0 additions & 24 deletions packages/oauth-provider/src/assets/app/app.tsx

This file was deleted.

42 changes: 25 additions & 17 deletions packages/oauth-provider/src/assets/app/backend-data.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import type { ClientMetadata, Session } from './types'

// This is injected by the backend in the HTML template
declare const __backendData:
| Readonly<{
clientId: string
clientMetadata: Readonly<ClientMetadata>
requestUri: string
csrfCookie: string
sessions: readonly Readonly<Session>[]
consentRequired: boolean
loginHint?: string
}>
| Readonly<{
error: string
error_description: string
}>
export type BrandingData = {
logo?: string
}

export const backendData = __backendData
export type ErrorData = {
error: string
error_description: string
}

export type BackendData = typeof __backendData
export type AuthorizeData = {
clientId: string
clientMetadata: ClientMetadata
requestUri: string
csrfCookie: string
sessions: Session[]
consentRequired: boolean
loginHint?: string
}

// These values are injected by the backend when it builds the
// page HTML.

export const brandingData = window['__brandingData'] as BrandingData | undefined
export const errorData = window['__errorData'] as ErrorData | undefined
export const authorizeData = window['__authorizeData'] as
| AuthorizeData
| undefined
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Account, ClientMetadata } from '../types'
import { Layout } from './layout'
import { PageLayout } from './page-layout'

export function GrantAccept({
export function AcceptPage({
account,
clientId,
clientMetadata,
Expand All @@ -20,7 +20,7 @@ export function GrantAccept({
const clientName = clientMetadata.client_name || clientUri || clientId

return (
<Layout
<PageLayout
title="Authorize"
subTitle={
<>
Expand All @@ -40,7 +40,7 @@ export function GrantAccept({
</div>
)}

<h1 className="text-2xl font-semibold text-center text-blue-600">
<h1 className="text-2xl font-semibold text-center text-primary">
{clientName}
</h1>

Expand All @@ -66,7 +66,7 @@ export function GrantAccept({
<button
type="button"
onClick={() => onBack()}
className="bg-transparent font-light text-blue-600 rounded-md py-2"
className="bg-transparent font-light text-primary rounded-md py-2"
>
Back
</button>
Expand All @@ -77,20 +77,20 @@ export function GrantAccept({
<button
type="button"
onClick={() => onReject()}
className="ml-2 bg-transparent text-blue-600 rounded-md py-2"
className="ml-2 bg-transparent text-primary rounded-md py-2"
>
Reject
</button>

<button
type="button"
onClick={() => onAccept()}
className="ml-2 bg-transparent text-blue-600 rounded-md py-2 font-semibold"
className="ml-2 bg-transparent text-primary rounded-md py-2 font-semibold"
>
Accept
</button>
</div>
</div>
</Layout>
</PageLayout>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Account } from '../types'
import { Layout } from './layout'
import { PageLayout } from './page-layout'

export function AccountList({
export function AccountSelectorPage({
accounts,
onAccount,
another = undefined,
Expand All @@ -14,7 +14,10 @@ export function AccountList({
onBack?: () => void
}) {
return (
<Layout title="Sign in as..." subTitle="Select from an existing account">
<PageLayout
title="Sign in as..."
subTitle="Select from an existing account"
>
<div className="max-w-lg w-full" {...props}>
<p className="font-medium p-4">Sign in as...</p>
<ul>
Expand Down Expand Up @@ -60,13 +63,13 @@ export function AccountList({
<button
type="button"
onClick={() => onBack()}
className="bg-transparent font-light text-blue-600 rounded-md py-2"
className="bg-transparent font-light text-primary rounded-md py-2"
>
Back
</button>
</div>
)}
</div>
</Layout>
</PageLayout>
)
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { useMemo, useState } from 'react'

import type { BackendData } from '../backend-data'
import type { AuthorizeData } from '../backend-data'
import { cookies } from '../cookies'
import { Account, Session } from '../types'

import { GrantAccept } from './grant-accept'
import { Layout } from './layout'
import { LoginForm } from './login-form'
import { SessionSelector } from './session-selector'
import { AcceptPage } from './accept-page'
import { PageLayout } from './page-layout'
import { LoginPage } from './login-page'
import { SessionSelectorPage } from './session-selector-page'

export function Authorize({
export function AuthorizePage({
requestUri,
clientId,
clientMetadata,
csrfCookie,
consentRequired: initialConsentRequired,
loginHint: initialLoginHint,
sessions: initialSessions,
}: Exclude<BackendData, { error: string }>) {
}: AuthorizeData) {
const csrfToken = useMemo(() => cookies[csrfCookie], [csrfCookie])
const [isDone, setIsDone] = useState(false)
const [loginHint, setLoginHint] = useState(initialLoginHint)
Expand Down Expand Up @@ -113,13 +113,15 @@ export function Authorize({

if (isDone) {
// TODO
return <Layout title="Login complete">You are being redirected</Layout>
return (
<PageLayout title="Login complete">You are being redirected</PageLayout>
)
}

if (selectedSession) {
if (selectedSession.loginRequired === false) {
return (
<GrantAccept
<AcceptPage
onBack={() => setSub(null)}
onAccept={() => authorizeAccept(selectedSession.account)}
onReject={() => authorizeReject()}
Expand All @@ -130,7 +132,7 @@ export function Authorize({
)
} else {
return (
<LoginForm
<LoginPage
username={selectedSession.account.preferred_username}
usernameReadonly={true}
onLogin={performLogin}
Expand All @@ -142,7 +144,7 @@ export function Authorize({

if (loginHint) {
return (
<LoginForm
<LoginPage
username={loginHint}
onLogin={performLogin}
onBack={() => setLoginHint(undefined)}
Expand All @@ -151,7 +153,7 @@ export function Authorize({
}

return (
<SessionSelector
<SessionSelectorPage
sessions={sessions}
onLogin={performLogin}
onSession={({ account }) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import type { BackendData } from '../backend-data'
import type { ErrorData } from '../backend-data'

import { Layout } from './layout'
import { PageLayout } from './page-layout'

export function Error({
error,
error_description,
}: Extract<BackendData, { error: string }>) {
export function ErrorPage({ error, error_description }: ErrorData) {
return (
<Layout title="An error occurred">
<PageLayout title="An error occurred">
<div
className="bg-red-100 border-t-4 border-red-500 rounded-b text-red-900 px-4 py-3 shadow-md"
role="alert"
Expand All @@ -28,6 +25,6 @@ export function Error({
</div>
</div>
</div>
</Layout>
</PageLayout>
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FormHTMLAttributes } from 'react'

import { Layout } from './layout'
import { PageLayout } from './page-layout'

export function LoginForm({
export function LoginPage({
onLogin,
onBack = undefined,
username = '',
Expand Down Expand Up @@ -37,7 +37,7 @@ export function LoginForm({
}

return (
<Layout title="Sign in" subTitle="Enter your username and password">
<PageLayout title="Sign in" subTitle="Enter your username and password">
<form {...props} className="max-w-lg w-full m-4" onSubmit={onSubmit}>
<fieldset className="rounded-md border border-solid border-slate-200 dark:border-slate-700 text-neutral-700 dark:text-neutral-100">
<div className="relative p-1 flex flex-wrap items-center justify-stretch">
Expand Down Expand Up @@ -77,7 +77,7 @@ export function LoginForm({
id="remember"
name="remember"
type="checkbox"
className="text-blue-600"
className="text-primary"
/>
</span>

Expand All @@ -93,7 +93,7 @@ export function LoginForm({
<div className="m-4 flex items-center justify-between">
<button
type="submit"
className="bg-transparent text-blue-600 rounded-md py-2 font-semibold order-last"
className="bg-transparent text-primary rounded-md py-2 font-semibold order-last"
>
Next
</button>
Expand All @@ -102,13 +102,13 @@ export function LoginForm({
<button
type="button"
onClick={onBack}
className="bg-transparent font-light text-blue-600 rounded-md py-2"
className="bg-transparent font-light text-primary rounded-md py-2"
>
Back
</button>
)}
</div>
</form>
</Layout>
</PageLayout>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HTMLAttributes } from 'react'

export function Layout({
export function PageLayout({
title,
subTitle,
children,
Expand All @@ -16,7 +16,7 @@ export function Layout({
{...props}
>
<div className="w-1/2 hidden p-4 md:grid content-center justify-items-end text-right bg-slate-100 dark:bg-transparent dark:border-r border-slate-200 dark:border-slate-700">
<h1 className="text-2xl mt-4 font-semibold mb-4 text-blue-600">
<h1 className="text-2xl mt-4 font-semibold mb-4 text-primary">
{title}
</h1>
<p className="min-h-16">{subTitle}</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState } from 'react'

import { Session } from '../types'
import { AccountList } from './account-list'
import { LoginForm } from './login-form'
import { AccountSelectorPage } from './account-selector-page'
import { LoginPage } from './login-page'

export function SessionSelector({
export function SessionSelectorPage({
sessions,
onSession,
onLogin,
Expand All @@ -22,7 +22,7 @@ export function SessionSelector({
const [showLogin, setShowLogin] = useState(sessions.length === 0)

return showLogin ? (
<LoginForm
<LoginPage
onLogin={onLogin}
onBack={
sessions.length > 0
Expand All @@ -33,7 +33,7 @@ export function SessionSelector({
}
/>
) : (
<AccountList
<AccountSelectorPage
accounts={sessions.map((s) => s.account)}
onAccount={(a) => {
const session = sessions.find((s) => s.account.sub === a.sub)
Expand Down
Loading

0 comments on commit 96985fe

Please sign in to comment.