@atproto/oauth-provider 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +13 -0
- package/dist/account/account-manager.d.ts +2 -2
- package/dist/account/account-manager.d.ts.map +1 -1
- package/dist/account/account-manager.js.map +1 -1
- package/dist/account/account-store.d.ts +19 -6
- package/dist/account/account-store.d.ts.map +1 -1
- package/dist/account/account-store.js +16 -1
- package/dist/account/account-store.js.map +1 -1
- package/dist/assets/app/bundle-manifest.json +3 -3
- package/dist/assets/app/main.css +1 -1
- package/dist/assets/app/main.js +3 -3
- package/dist/assets/app/main.js.map +1 -1
- package/dist/client/client-auth.d.ts.map +1 -1
- package/dist/client/client-auth.js +2 -2
- package/dist/client/client-auth.js.map +1 -1
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js +3 -1
- package/dist/client/client-manager.js.map +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +3 -3
- package/dist/client/client.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts.map +1 -1
- package/dist/dpop/dpop-manager.js +3 -3
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/errors/invalid-token-error.d.ts.map +1 -1
- package/dist/errors/invalid-token-error.js +3 -2
- package/dist/errors/invalid-token-error.js.map +1 -1
- package/dist/errors/second-authentication-factor-required-error.d.ts +13 -0
- package/dist/errors/second-authentication-factor-required-error.d.ts.map +1 -0
- package/dist/errors/second-authentication-factor-required-error.js +23 -0
- package/dist/errors/second-authentication-factor-required-error.js.map +1 -0
- package/dist/lib/util/authorization-header.js +1 -1
- package/dist/lib/util/authorization-header.js.map +1 -1
- package/dist/metadata/build-metadata.d.ts.map +1 -1
- package/dist/metadata/build-metadata.js +2 -0
- package/dist/metadata/build-metadata.js.map +1 -1
- package/dist/oauth-errors.d.ts +1 -0
- package/dist/oauth-errors.d.ts.map +1 -1
- package/dist/oauth-errors.js +3 -1
- package/dist/oauth-errors.js.map +1 -1
- package/dist/oauth-provider.d.ts +101 -4
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +98 -110
- package/dist/oauth-provider.js.map +1 -1
- package/dist/output/{send-authorize-page.d.ts → build-authorize-data.d.ts} +2 -5
- package/dist/output/build-authorize-data.d.ts.map +1 -0
- package/dist/output/build-authorize-data.js +22 -0
- package/dist/output/build-authorize-data.js.map +1 -0
- package/dist/output/build-error-payload.d.ts.map +1 -1
- package/dist/output/build-error-payload.js +4 -3
- package/dist/output/build-error-payload.js.map +1 -1
- package/dist/output/customization.d.ts +2 -12
- package/dist/output/customization.d.ts.map +1 -1
- package/dist/output/customization.js +59 -32
- package/dist/output/customization.js.map +1 -1
- package/dist/output/output-manager.d.ts +16 -0
- package/dist/output/output-manager.d.ts.map +1 -0
- package/dist/output/output-manager.js +69 -0
- package/dist/output/output-manager.js.map +1 -0
- package/dist/output/send-web-page.d.ts +1 -1
- package/dist/output/send-web-page.d.ts.map +1 -1
- package/dist/output/send-web-page.js +3 -2
- package/dist/output/send-web-page.js.map +1 -1
- package/package.json +6 -6
- package/src/account/account-manager.ts +2 -2
- package/src/account/account-store.ts +12 -6
- package/src/assets/app/components/accept-form.tsx +86 -83
- package/src/assets/app/components/account-picker.tsx +98 -79
- package/src/assets/app/components/button.tsx +34 -0
- package/src/assets/app/components/client-identifier.tsx +12 -13
- package/src/assets/app/components/fieldset.tsx +26 -0
- package/src/assets/app/components/form-card.tsx +47 -0
- package/src/assets/app/components/help-card.tsx +1 -1
- package/src/assets/app/components/icons/alert-icon.tsx +5 -0
- package/src/assets/app/components/icons/at-symbol-icon.tsx +5 -0
- package/src/assets/app/components/icons/caret-right-icon.tsx +5 -0
- package/src/assets/app/components/icons/lock-icon.tsx +5 -0
- package/src/assets/app/components/icons/token-icon.tsx +5 -0
- package/src/assets/app/components/icons/util.tsx +17 -0
- package/src/assets/app/components/info-card.tsx +45 -0
- package/src/assets/app/components/input-checkbox.tsx +47 -0
- package/src/assets/app/components/input-container.tsx +37 -0
- package/src/assets/app/components/input-layout.tsx +47 -0
- package/src/assets/app/components/input-text.tsx +69 -0
- package/src/assets/app/components/layout-title-page.tsx +33 -16
- package/src/assets/app/components/layout-welcome.tsx +30 -14
- package/src/assets/app/components/sign-in-form.tsx +214 -196
- package/src/assets/app/components/sign-up-account-form.tsx +101 -117
- package/src/assets/app/components/sign-up-disclaimer.tsx +1 -1
- package/src/assets/app/hooks/use-api.ts +2 -0
- package/src/assets/app/lib/api.ts +49 -14
- package/src/assets/app/lib/clsx.ts +6 -1
- package/src/assets/app/lib/util.ts +3 -0
- package/src/assets/app/main.css +2 -1
- package/src/assets/app/views/accept-view.tsx +4 -3
- package/src/assets/app/views/authorize-view.tsx +8 -4
- package/src/assets/app/views/error-view.tsx +24 -15
- package/src/assets/app/views/sign-in-view.tsx +5 -15
- package/src/assets/app/views/sign-up-view.tsx +3 -10
- package/src/assets/app/views/welcome-view.tsx +11 -18
- package/src/client/client-auth.ts +3 -2
- package/src/client/client-manager.ts +2 -1
- package/src/client/client.ts +3 -1
- package/src/dpop/dpop-manager.ts +3 -2
- package/src/errors/invalid-token-error.ts +3 -1
- package/src/errors/second-authentication-factor-required-error.ts +25 -0
- package/src/lib/util/authorization-header.ts +1 -1
- package/src/metadata/build-metadata.ts +3 -0
- package/src/oauth-errors.ts +1 -0
- package/src/oauth-provider.ts +110 -99
- package/src/output/{send-authorize-page.ts → build-authorize-data.ts} +3 -43
- package/src/output/build-error-payload.ts +3 -1
- package/src/output/customization.ts +67 -45
- package/src/output/output-manager.ts +87 -0
- package/src/output/send-web-page.ts +4 -3
- package/tailwind.config.js +14 -1
- package/dist/output/send-authorize-page.d.ts.map +0 -1
- package/dist/output/send-authorize-page.js +0 -49
- package/dist/output/send-authorize-page.js.map +0 -1
- package/dist/output/send-error-page.d.ts +0 -5
- package/dist/output/send-error-page.d.ts.map +0 -1
- package/dist/output/send-error-page.js +0 -31
- package/dist/output/send-error-page.js.map +0 -1
- package/src/output/send-error-page.ts +0 -41
@@ -6,36 +6,41 @@ import {
|
|
6
6
|
useState,
|
7
7
|
} from 'react'
|
8
8
|
|
9
|
-
import {
|
10
|
-
import {
|
9
|
+
import { Button } from './button'
|
10
|
+
import { FormCard, FormCardProps } from './form-card'
|
11
|
+
import { Override } from '../lib/util'
|
12
|
+
import { Fieldset } from './fieldset'
|
11
13
|
|
12
14
|
export type SignUpAccountFormOutput = {
|
13
15
|
username: string
|
14
16
|
password: string
|
15
17
|
}
|
16
18
|
|
17
|
-
export type SignUpAccountFormProps =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
19
|
+
export type SignUpAccountFormProps = Override<
|
20
|
+
FormCardProps,
|
21
|
+
{
|
22
|
+
onSubmit: (credentials: SignUpAccountFormOutput) => void | PromiseLike<void>
|
23
|
+
submitLabel?: ReactNode
|
24
|
+
submitAria?: string
|
25
|
+
|
26
|
+
onCancel?: () => void
|
27
|
+
cancelLabel?: ReactNode
|
28
|
+
cancelAria?: string
|
29
|
+
|
30
|
+
username?: string
|
31
|
+
usernamePlaceholder?: string
|
32
|
+
usernameLabel?: string
|
33
|
+
usernameAria?: string
|
34
|
+
usernamePattern?: string
|
35
|
+
usernameTitle?: string
|
36
|
+
|
37
|
+
passwordPlaceholder?: string
|
38
|
+
passwordLabel?: string
|
39
|
+
passwordAria?: string
|
40
|
+
passwordPattern?: string
|
41
|
+
passwordTitle?: string
|
42
|
+
}
|
43
|
+
>
|
39
44
|
|
40
45
|
export function SignUpAccountForm({
|
41
46
|
onSubmit,
|
@@ -59,9 +64,8 @@ export function SignUpAccountForm({
|
|
59
64
|
passwordPattern,
|
60
65
|
passwordTitle,
|
61
66
|
|
62
|
-
className,
|
63
67
|
children,
|
64
|
-
...
|
68
|
+
...props
|
65
69
|
}: SignUpAccountFormProps &
|
66
70
|
Omit<FormHTMLAttributes<HTMLFormElement>, keyof SignUpAccountFormProps>) {
|
67
71
|
const [loading, setLoading] = useState(false)
|
@@ -98,104 +102,84 @@ export function SignUpAccountForm({
|
|
98
102
|
)
|
99
103
|
|
100
104
|
return (
|
101
|
-
<
|
102
|
-
{
|
103
|
-
|
105
|
+
<FormCard
|
106
|
+
append={children}
|
107
|
+
error={errorMessage}
|
104
108
|
onSubmit={doSubmit}
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
<span className="w-6 ml-1 text-center text-base">@</span>
|
116
|
-
<input
|
117
|
-
name="username"
|
118
|
-
type="text"
|
119
|
-
onChange={() => setErrorMessage(null)}
|
120
|
-
className="relative m-1 block w-[1px] min-w-0 flex-auto leading-[1.6] bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder:text-neutral-100 disabled:text-gray-500"
|
121
|
-
placeholder={usernamePlaceholder}
|
122
|
-
aria-label={usernameAria}
|
123
|
-
autoCapitalize="none"
|
124
|
-
autoCorrect="off"
|
125
|
-
autoComplete="username"
|
126
|
-
spellCheck="false"
|
127
|
-
dir="auto"
|
128
|
-
enterKeyHint="next"
|
129
|
-
required
|
130
|
-
defaultValue={defaultUsername}
|
131
|
-
pattern={usernamePattern}
|
132
|
-
title={usernameTitle}
|
133
|
-
/>
|
134
|
-
</div>
|
135
|
-
|
136
|
-
<label className="text-sm font-medium" htmlFor="password">
|
137
|
-
{passwordLabel}
|
138
|
-
</label>
|
139
|
-
|
140
|
-
<div
|
141
|
-
id="password"
|
142
|
-
className="mb-4 relative flex flex-wrap items-center justify-stretch rounded-md border border-solid border-slate-200 dark:border-slate-700 text-neutral-700 dark:text-neutral-100"
|
143
|
-
>
|
144
|
-
<span className="w-6 ml-1 text-center text-2xl font-light -mb-2">
|
145
|
-
*
|
146
|
-
</span>
|
147
|
-
<input
|
148
|
-
name="password"
|
149
|
-
type="password"
|
150
|
-
onChange={() => setErrorMessage(null)}
|
151
|
-
className="relative m-1 block w-[1px] min-w-0 flex-auto leading-[1.6] bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder:text-neutral-100"
|
152
|
-
placeholder={passwordPlaceholder}
|
153
|
-
aria-label={passwordAria}
|
154
|
-
autoCapitalize="none"
|
155
|
-
autoCorrect="off"
|
156
|
-
autoComplete="new-password"
|
157
|
-
dir="auto"
|
158
|
-
enterKeyHint="done"
|
159
|
-
spellCheck="false"
|
160
|
-
required
|
161
|
-
pattern={passwordPattern}
|
162
|
-
title={passwordTitle}
|
163
|
-
/>
|
164
|
-
</div>
|
165
|
-
</fieldset>
|
166
|
-
|
167
|
-
{children && <div className="mt-4">{children}</div>}
|
168
|
-
|
169
|
-
{errorMessage && <ErrorCard className="mt-2" message={errorMessage} />}
|
170
|
-
|
171
|
-
<div className="flex-auto"></div>
|
172
|
-
|
173
|
-
<div className="p-4 flex flex-wrap items-center justify-start">
|
174
|
-
<button
|
175
|
-
className="py-2 bg-transparent text-primary rounded-md font-semibold order-last"
|
109
|
+
cancel={
|
110
|
+
onCancel && (
|
111
|
+
<Button aria-label={cancelAria} onClick={onCancel}>
|
112
|
+
{cancelLabel}
|
113
|
+
</Button>
|
114
|
+
)
|
115
|
+
}
|
116
|
+
actions={
|
117
|
+
<Button
|
118
|
+
color="brand"
|
176
119
|
type="submit"
|
177
|
-
role="Button"
|
178
120
|
aria-label={submitAria}
|
179
121
|
disabled={loading}
|
180
122
|
>
|
181
123
|
{submitLabel}
|
182
|
-
</
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
124
|
+
</Button>
|
125
|
+
}
|
126
|
+
{...props}
|
127
|
+
>
|
128
|
+
<Fieldset disabled={loading}>
|
129
|
+
<label className="text-sm font-medium block">
|
130
|
+
<p>{usernameLabel}</p>
|
131
|
+
|
132
|
+
<div className="relative flex flex-wrap items-center justify-stretch rounded-md border border-solid border-slate-200 dark:border-slate-700 text-neutral-700 dark:text-neutral-100">
|
133
|
+
<span className="w-6 ml-1 text-center text-base">@</span>
|
134
|
+
<input
|
135
|
+
name="username"
|
136
|
+
type="text"
|
137
|
+
onChange={() => setErrorMessage(null)}
|
138
|
+
className="relative m-1 block w-[1px] min-w-0 flex-auto leading-[1.6] bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder:text-neutral-100 disabled:text-gray-500"
|
139
|
+
placeholder={usernamePlaceholder}
|
140
|
+
aria-label={usernameAria}
|
141
|
+
autoCapitalize="none"
|
142
|
+
autoCorrect="off"
|
143
|
+
autoComplete="username"
|
144
|
+
spellCheck="false"
|
145
|
+
dir="auto"
|
146
|
+
enterKeyHint="next"
|
147
|
+
required
|
148
|
+
defaultValue={defaultUsername}
|
149
|
+
pattern={usernamePattern}
|
150
|
+
title={usernameTitle}
|
151
|
+
/>
|
152
|
+
</div>
|
153
|
+
</label>
|
195
154
|
|
196
|
-
<
|
197
|
-
|
198
|
-
|
155
|
+
<label className="text-sm font-medium block">
|
156
|
+
<p>{passwordLabel}</p>
|
157
|
+
|
158
|
+
<div className="relative flex flex-wrap items-center justify-stretch rounded-md border border-solid border-slate-200 dark:border-slate-700 text-neutral-700 dark:text-neutral-100">
|
159
|
+
<span className="w-6 ml-1 text-center text-2xl font-light -mb-2">
|
160
|
+
*
|
161
|
+
</span>
|
162
|
+
<input
|
163
|
+
name="password"
|
164
|
+
type="password"
|
165
|
+
onChange={() => setErrorMessage(null)}
|
166
|
+
className="relative m-1 block w-[1px] min-w-0 flex-auto leading-[1.6] bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder:text-neutral-100"
|
167
|
+
placeholder={passwordPlaceholder}
|
168
|
+
aria-label={passwordAria}
|
169
|
+
autoCapitalize="none"
|
170
|
+
autoCorrect="off"
|
171
|
+
autoComplete="new-password"
|
172
|
+
dir="auto"
|
173
|
+
enterKeyHint="done"
|
174
|
+
spellCheck="false"
|
175
|
+
required
|
176
|
+
pattern={passwordPattern}
|
177
|
+
title={passwordTitle}
|
178
|
+
/>
|
179
|
+
</div>
|
180
|
+
</label>
|
181
|
+
</Fieldset>
|
182
|
+
</FormCard>
|
199
183
|
)
|
200
184
|
}
|
201
185
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { FetchResponseError, Json } from '@atproto-labs/fetch'
|
2
2
|
|
3
3
|
import { Account, Session } from '../backend-data'
|
4
4
|
|
@@ -15,7 +15,7 @@ export class Api {
|
|
15
15
|
password: string
|
16
16
|
remember?: boolean
|
17
17
|
}): Promise<Session> {
|
18
|
-
const
|
18
|
+
const response = await fetch('/oauth/authorize/sign-in', {
|
19
19
|
method: 'POST',
|
20
20
|
headers: { 'Content-Type': 'application/json' },
|
21
21
|
mode: 'same-origin',
|
@@ -26,20 +26,40 @@ export class Api {
|
|
26
26
|
credentials,
|
27
27
|
}),
|
28
28
|
})
|
29
|
-
.then(fetchOkProcessor())
|
30
|
-
.then(
|
31
|
-
fetchJsonProcessor<{
|
32
|
-
account: Account
|
33
|
-
consentRequired: boolean
|
34
|
-
}>(),
|
35
|
-
)
|
36
29
|
|
37
|
-
|
38
|
-
account: json.account,
|
30
|
+
const json: Json = await response.json()
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
if (response.ok) {
|
33
|
+
const data = json as {
|
34
|
+
account: Account
|
35
|
+
consentRequired: boolean
|
36
|
+
}
|
37
|
+
|
38
|
+
return {
|
39
|
+
account: data.account,
|
40
|
+
|
41
|
+
selected: true,
|
42
|
+
loginRequired: false,
|
43
|
+
consentRequired: this.newSessionsRequireConsent || data.consentRequired,
|
44
|
+
}
|
45
|
+
} else if (
|
46
|
+
response.status === 400 &&
|
47
|
+
json?.['error'] === 'invalid_request' &&
|
48
|
+
json?.['error_description'] === 'Invalid credentials'
|
49
|
+
) {
|
50
|
+
throw new InvalidCredentialsError()
|
51
|
+
} else if (
|
52
|
+
response.status === 401 &&
|
53
|
+
json?.['error'] === 'second_authentication_factor_required'
|
54
|
+
) {
|
55
|
+
const data = json as {
|
56
|
+
type: 'emailOtp'
|
57
|
+
hint: string
|
58
|
+
}
|
59
|
+
|
60
|
+
throw new SecondAuthenticationFactorRequiredError(data.type, data.hint)
|
61
|
+
} else {
|
62
|
+
throw new FetchResponseError(response)
|
43
63
|
}
|
44
64
|
}
|
45
65
|
|
@@ -62,3 +82,18 @@ export class Api {
|
|
62
82
|
return url
|
63
83
|
}
|
64
84
|
}
|
85
|
+
|
86
|
+
export class InvalidCredentialsError extends Error {
|
87
|
+
constructor() {
|
88
|
+
super('Invalid credentials')
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
export class SecondAuthenticationFactorRequiredError extends Error {
|
93
|
+
constructor(
|
94
|
+
public type: 'emailOtp',
|
95
|
+
public hint: string,
|
96
|
+
) {
|
97
|
+
super(`${type} authentication factor required (hint: ${hint})`)
|
98
|
+
}
|
99
|
+
}
|
@@ -1,4 +1,9 @@
|
|
1
|
-
export function clsx(
|
1
|
+
export function clsx(
|
2
|
+
a?: string,
|
3
|
+
...args: readonly (string | undefined)[]
|
4
|
+
): string | undefined {
|
5
|
+
if (args.length === 0) return a
|
6
|
+
const b = clsx(...args)
|
2
7
|
if (a && b) return `${a} ${b}`
|
3
8
|
return a || b
|
4
9
|
}
|
package/src/assets/app/main.css
CHANGED
@@ -31,13 +31,14 @@ export function AcceptView({
|
|
31
31
|
subtitle={
|
32
32
|
<>
|
33
33
|
Grant access to your{' '}
|
34
|
-
<b
|
35
|
-
|
34
|
+
<b className="text-black dark:text-white">
|
35
|
+
{account.preferred_username || account.email || account.sub}
|
36
|
+
</b>{' '}
|
37
|
+
account
|
36
38
|
</>
|
37
39
|
}
|
38
40
|
>
|
39
41
|
<AcceptForm
|
40
|
-
className="max-w-lg w-full"
|
41
42
|
clientId={clientId}
|
42
43
|
clientMetadata={clientMetadata}
|
43
44
|
clientTrusted={clientTrusted}
|
@@ -85,10 +85,14 @@ export function AuthorizeView({
|
|
85
85
|
clientTrusted={authorizeData.clientTrusted}
|
86
86
|
onAccept={() => doAccept(session.account)}
|
87
87
|
onReject={doReject}
|
88
|
-
onBack={
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
onBack={
|
89
|
+
forceSignIn
|
90
|
+
? undefined
|
91
|
+
: () => {
|
92
|
+
setSession(null)
|
93
|
+
setView(sessions.length ? 'sign-in' : 'welcome')
|
94
|
+
}
|
95
|
+
}
|
92
96
|
/>
|
93
97
|
)
|
94
98
|
}
|
@@ -1,27 +1,36 @@
|
|
1
1
|
import { CustomizationData, ErrorData } from '../backend-data'
|
2
|
-
import {
|
3
|
-
import { LayoutWelcome } from '../components/layout-welcome'
|
2
|
+
import { InfoCard } from '../components/info-card'
|
3
|
+
import { LayoutWelcome, LayoutWelcomeProps } from '../components/layout-welcome'
|
4
|
+
import { Override } from '../lib/util'
|
4
5
|
|
5
|
-
export type ErrorViewProps =
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
export type ErrorViewProps = Override<
|
7
|
+
Omit<LayoutWelcomeProps, keyof CustomizationData>,
|
8
|
+
{
|
9
|
+
customizationData?: CustomizationData
|
10
|
+
errorData?: ErrorData
|
11
|
+
}
|
12
|
+
>
|
9
13
|
|
10
|
-
export function ErrorView({
|
14
|
+
export function ErrorView({
|
15
|
+
errorData,
|
16
|
+
customizationData,
|
17
|
+
...props
|
18
|
+
}: ErrorViewProps) {
|
11
19
|
return (
|
12
|
-
<LayoutWelcome {...customizationData}>
|
13
|
-
<
|
20
|
+
<LayoutWelcome {...customizationData} {...props}>
|
21
|
+
<InfoCard role="alert">{getUserFriendlyMessage(errorData)}</InfoCard>
|
14
22
|
</LayoutWelcome>
|
15
23
|
)
|
16
24
|
}
|
17
25
|
|
18
26
|
function getUserFriendlyMessage(errorData?: ErrorData) {
|
19
27
|
const desc = errorData?.error_description
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
if (
|
29
|
+
desc === 'This request has expired' ||
|
30
|
+
desc?.startsWith('Unknown request_uri') // Request was removed from database
|
31
|
+
) {
|
32
|
+
return 'This sign-in session has expired'
|
33
|
+
} else {
|
34
|
+
return desc || 'An unknown error occurred'
|
26
35
|
}
|
27
36
|
}
|
@@ -44,11 +44,12 @@ export function SignInView({
|
|
44
44
|
subtitle="Confirm your password to continue"
|
45
45
|
>
|
46
46
|
<SignInForm
|
47
|
-
className="max-w-lg w-full"
|
48
47
|
onSubmit={onSignIn}
|
49
48
|
onCancel={clearSession}
|
50
49
|
cancelAria="Back" // to account picker
|
51
|
-
usernameDefault={
|
50
|
+
usernameDefault={
|
51
|
+
session.account.preferred_username || session.account.sub
|
52
|
+
}
|
52
53
|
usernameReadonly={true}
|
53
54
|
rememberDefault={true}
|
54
55
|
/>
|
@@ -60,7 +61,6 @@ export function SignInView({
|
|
60
61
|
return (
|
61
62
|
<LayoutTitlePage title="Sign in" subtitle="Enter your password">
|
62
63
|
<SignInForm
|
63
|
-
className="max-w-lg w-full"
|
64
64
|
onSubmit={onSignIn}
|
65
65
|
onCancel={onBack}
|
66
66
|
cancelAria="Back"
|
@@ -77,12 +77,7 @@ export function SignInView({
|
|
77
77
|
title="Sign in"
|
78
78
|
subtitle="Enter your username and password"
|
79
79
|
>
|
80
|
-
<SignInForm
|
81
|
-
className="max-w-lg w-full"
|
82
|
-
onSubmit={onSignIn}
|
83
|
-
onCancel={onBack}
|
84
|
-
cancelAria="Back"
|
85
|
-
/>
|
80
|
+
<SignInForm onSubmit={onSignIn} onCancel={onBack} cancelAria="Back" />
|
86
81
|
</LayoutTitlePage>
|
87
82
|
)
|
88
83
|
}
|
@@ -94,7 +89,6 @@ export function SignInView({
|
|
94
89
|
subtitle="Enter your username and password"
|
95
90
|
>
|
96
91
|
<SignInForm
|
97
|
-
className="max-w-lg w-full"
|
98
92
|
onSubmit={onSignIn}
|
99
93
|
onCancel={() => setShowSignInForm(false)}
|
100
94
|
cancelAria="Back" // to account picker
|
@@ -104,12 +98,8 @@ export function SignInView({
|
|
104
98
|
}
|
105
99
|
|
106
100
|
return (
|
107
|
-
<LayoutTitlePage
|
108
|
-
title="Sign in as..."
|
109
|
-
subtitle="Select an account to continue."
|
110
|
-
>
|
101
|
+
<LayoutTitlePage title="Sign in" subtitle="Select from an existing account">
|
111
102
|
<AccountPicker
|
112
|
-
className="max-w-lg w-full"
|
113
103
|
accounts={accounts}
|
114
104
|
onAccount={(a) => setSession(a.sub)}
|
115
105
|
onOther={() => setShowSignInForm(true)}
|
@@ -8,6 +8,7 @@ import {
|
|
8
8
|
SignUpAccountFormOutput,
|
9
9
|
} from '../components/sign-up-account-form'
|
10
10
|
import { SignUpDisclaimer } from '../components/sign-up-disclaimer'
|
11
|
+
import { Button } from '../components/button'
|
11
12
|
|
12
13
|
export type SignUpViewProps = {
|
13
14
|
stepName?: (step: number, total: number) => ReactNode
|
@@ -57,7 +58,7 @@ export function SignUpView({
|
|
57
58
|
title="Create Account"
|
58
59
|
subtitle="We're so excited to have you join us!"
|
59
60
|
>
|
60
|
-
<div className="
|
61
|
+
<div className="flex flex-col">
|
61
62
|
<p className="mt-4 text-slate-400 dark:text-slate-600">
|
62
63
|
{stepName(step, stepCount)}
|
63
64
|
</p>
|
@@ -76,15 +77,7 @@ export function SignUpView({
|
|
76
77
|
</SignUpAccountForm>
|
77
78
|
)}
|
78
79
|
|
79
|
-
{step === 2 && (
|
80
|
-
<button
|
81
|
-
type="button"
|
82
|
-
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-slate-700 bg-slate-100 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500"
|
83
|
-
onClick={() => setStep(1)}
|
84
|
-
>
|
85
|
-
Back
|
86
|
-
</button>
|
87
|
-
)}
|
80
|
+
{step === 2 && <Button onClick={() => setStep(1)}>Back</Button>}
|
88
81
|
|
89
82
|
<HelpCard className="mb-4" links={links} />
|
90
83
|
</div>
|
@@ -1,5 +1,5 @@
|
|
1
|
+
import { Button } from '../components/button'
|
1
2
|
import { LayoutWelcome, LayoutWelcomeProps } from '../components/layout-welcome'
|
2
|
-
import { clsx } from '../lib/clsx'
|
3
3
|
|
4
4
|
export type WelcomeViewParams = LayoutWelcomeProps & {
|
5
5
|
onSignIn?: () => void
|
@@ -25,36 +25,29 @@ export function WelcomeView({
|
|
25
25
|
return (
|
26
26
|
<LayoutWelcome {...props}>
|
27
27
|
{onSignUp && (
|
28
|
-
<
|
29
|
-
className={
|
30
|
-
|
31
|
-
onSignIn ? 'bg-primary' : 'bg-slate-400',
|
32
|
-
)}
|
28
|
+
<Button
|
29
|
+
className={'m-1 w-60 max-w-full'}
|
30
|
+
color={onSignIn ? 'brand' : undefined}
|
33
31
|
onClick={onSignUp}
|
34
32
|
>
|
35
33
|
{signUpLabel}
|
36
|
-
</
|
34
|
+
</Button>
|
37
35
|
)}
|
38
36
|
|
39
37
|
{onSignIn && (
|
40
|
-
<
|
41
|
-
className={
|
42
|
-
|
43
|
-
onSignUp ? 'bg-slate-100 dark:bg-slate-700' : 'bg-primary',
|
44
|
-
)}
|
38
|
+
<Button
|
39
|
+
className={'m-1 w-60 max-w-full'}
|
40
|
+
color={onSignUp ? undefined : 'brand'}
|
45
41
|
onClick={onSignIn}
|
46
42
|
>
|
47
43
|
{signInLabel}
|
48
|
-
</
|
44
|
+
</Button>
|
49
45
|
)}
|
50
46
|
|
51
47
|
{onCancel && (
|
52
|
-
<
|
53
|
-
className="m-1 w-60 max-w-full bg-transparent text-primary py-2 px-4 rounded-full truncate font-light"
|
54
|
-
onClick={onCancel}
|
55
|
-
>
|
48
|
+
<Button className="m-1 w-60 max-w-full" onClick={onCancel}>
|
56
49
|
{cancelLabel}
|
57
|
-
</
|
50
|
+
</Button>
|
58
51
|
)}
|
59
52
|
</LayoutWelcome>
|
60
53
|
)
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import { CLIENT_ASSERTION_TYPE_JWT_BEARER } from '@atproto/oauth-types'
|
2
|
-
import { KeyLike, calculateJwkThumbprint, exportJWK } from 'jose'
|
3
|
-
import { JOSEError } from 'jose/errors'
|
2
|
+
import { KeyLike, calculateJwkThumbprint, errors, exportJWK } from 'jose'
|
4
3
|
|
5
4
|
import { InvalidClientError } from '../errors/invalid-client-error.js'
|
6
5
|
|
6
|
+
const { JOSEError } = errors
|
7
|
+
|
7
8
|
export type ClientAuth =
|
8
9
|
| { method: 'none' }
|
9
10
|
| {
|
@@ -39,7 +39,8 @@ import { Client } from './client.js'
|
|
39
39
|
|
40
40
|
const fetchMetadataHandler = pipe(
|
41
41
|
fetchOkProcessor(),
|
42
|
-
|
42
|
+
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html#section-4.1
|
43
|
+
fetchJsonProcessor('application/json', true),
|
43
44
|
fetchJsonZodProcessor(oauthClientMetadataSchema),
|
44
45
|
)
|
45
46
|
|