@shipfox/client-auth 5.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +43 -0
- package/dist/components/auth-guard.d.ts.map +1 -1
- package/dist/components/auth-guard.js +4 -4
- package/dist/components/auth-guard.js.map +1 -1
- package/dist/components/redirect-target.d.ts +7 -0
- package/dist/components/redirect-target.d.ts.map +1 -1
- package/dist/components/redirect-target.js +35 -4
- package/dist/components/redirect-target.js.map +1 -1
- package/dist/core/auth.d.ts +57 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +3 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/hooks/api/auth-mapper.d.ts +2 -17
- package/dist/hooks/api/auth-mapper.d.ts.map +1 -1
- package/dist/hooks/api/auth-mapper.js.map +1 -1
- package/dist/hooks/api/login-auth.d.ts +3 -6
- package/dist/hooks/api/login-auth.d.ts.map +1 -1
- package/dist/hooks/api/login-auth.js +2 -2
- package/dist/hooks/api/login-auth.js.map +1 -1
- package/dist/hooks/api/logout-auth.js +2 -2
- package/dist/hooks/api/logout-auth.js.map +1 -1
- package/dist/hooks/api/password-reset-auth.d.ts +5 -10
- package/dist/hooks/api/password-reset-auth.d.ts.map +1 -1
- package/dist/hooks/api/password-reset-auth.js +9 -6
- package/dist/hooks/api/password-reset-auth.js.map +1 -1
- package/dist/hooks/api/refresh-auth.d.ts +1 -1
- package/dist/hooks/api/refresh-auth.d.ts.map +1 -1
- package/dist/hooks/api/refresh-auth.js +1 -1
- package/dist/hooks/api/refresh-auth.js.map +1 -1
- package/dist/hooks/api/signup-auth.d.ts +2 -6
- package/dist/hooks/api/signup-auth.d.ts.map +1 -1
- package/dist/hooks/api/signup-auth.js +9 -2
- package/dist/hooks/api/signup-auth.js.map +1 -1
- package/dist/hooks/api/verify-email-auth.d.ts +4 -10
- package/dist/hooks/api/verify-email-auth.d.ts.map +1 -1
- package/dist/hooks/api/verify-email-auth.js +15 -5
- package/dist/hooks/api/verify-email-auth.js.map +1 -1
- package/dist/hooks/api/workspace-auth.d.ts +5 -7
- package/dist/hooks/api/workspace-auth.d.ts.map +1 -1
- package/dist/hooks/api/workspace-auth.js +6 -4
- package/dist/hooks/api/workspace-auth.js.map +1 -1
- package/dist/hooks/api/workspace-mapper.d.ts +1 -5
- package/dist/hooks/api/workspace-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workspace-mapper.js.map +1 -1
- package/dist/pages/login-page.d.ts.map +1 -1
- package/dist/pages/login-page.js +4 -5
- package/dist/pages/login-page.js.map +1 -1
- package/dist/pages/logout-page.d.ts.map +1 -1
- package/dist/pages/logout-page.js +8 -18
- package/dist/pages/logout-page.js.map +1 -1
- package/dist/pages/password-reset-page.d.ts.map +1 -1
- package/dist/pages/password-reset-page.js +8 -7
- package/dist/pages/password-reset-page.js.map +1 -1
- package/dist/pages/signup-page.d.ts.map +1 -1
- package/dist/pages/signup-page.js +16 -10
- package/dist/pages/signup-page.js.map +1 -1
- package/dist/pages/workspace-onboarding-page.js +2 -2
- package/dist/pages/workspace-onboarding-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +9 -0
- package/dist/routes/inputs.d.ts.map +1 -0
- package/dist/routes/inputs.js +17 -0
- package/dist/routes/inputs.js.map +1 -0
- package/dist/routes/login.d.ts +2 -0
- package/dist/routes/login.d.ts.map +1 -1
- package/dist/routes/login.js +2 -0
- package/dist/routes/login.js.map +1 -1
- package/dist/routes/logout.d.ts +2 -0
- package/dist/routes/logout.d.ts.map +1 -1
- package/dist/routes/logout.js +2 -0
- package/dist/routes/logout.js.map +1 -1
- package/dist/routes/reset.d.ts +2 -0
- package/dist/routes/reset.d.ts.map +1 -1
- package/dist/routes/reset.js +2 -0
- package/dist/routes/reset.js.map +1 -1
- package/dist/routes/signup.d.ts +2 -0
- package/dist/routes/signup.d.ts.map +1 -1
- package/dist/routes/signup.js +2 -0
- package/dist/routes/signup.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +11 -12
- package/src/components/auth-guard.tsx +4 -2
- package/src/components/redirect-target.test.ts +32 -1
- package/src/components/redirect-target.ts +41 -4
- package/src/core/auth.ts +55 -0
- package/src/hooks/api/auth-mapper.ts +2 -12
- package/src/hooks/api/login-auth.ts +4 -3
- package/src/hooks/api/logout-auth.ts +2 -2
- package/src/hooks/api/password-reset-auth.test.ts +2 -2
- package/src/hooks/api/password-reset-auth.ts +10 -10
- package/src/hooks/api/refresh-auth.ts +1 -0
- package/src/hooks/api/signup-auth.ts +9 -3
- package/src/hooks/api/verify-email-auth.ts +19 -9
- package/src/hooks/api/workspace-auth.ts +10 -5
- package/src/hooks/api/workspace-mapper.ts +1 -6
- package/src/pages/login-page.tsx +4 -3
- package/src/pages/logout-page.test.tsx +75 -0
- package/src/pages/logout-page.tsx +9 -13
- package/src/pages/password-reset-page.tsx +5 -5
- package/src/pages/signup-page.tsx +14 -8
- package/src/pages/workspace-onboarding-page.tsx +2 -2
- package/src/routes/inputs.test.ts +17 -0
- package/src/routes/inputs.ts +21 -0
- package/src/routes/login.tsx +2 -0
- package/src/routes/logout.tsx +2 -1
- package/src/routes/reset.tsx +2 -0
- package/src/routes/signup.tsx +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {signupBodySchema} from '@shipfox/api-auth-dto';
|
|
2
|
-
import {AuthShell} from '@shipfox/client-shell/runtime';
|
|
2
|
+
import {AuthShell, useRouteSearch} from '@shipfox/client-shell/runtime';
|
|
3
3
|
import {displayNameFieldError} from '@shipfox/client-ui';
|
|
4
4
|
import {Button, ButtonLink} from '@shipfox/react-ui/button';
|
|
5
5
|
import {Callout} from '@shipfox/react-ui/callout';
|
|
@@ -8,7 +8,7 @@ import {Icon} from '@shipfox/react-ui/icon';
|
|
|
8
8
|
import {toast} from '@shipfox/react-ui/toast';
|
|
9
9
|
import {Text} from '@shipfox/react-ui/typography';
|
|
10
10
|
import {useForm} from '@tanstack/react-form';
|
|
11
|
-
import {Link, useNavigate
|
|
11
|
+
import {Link, useNavigate} from '@tanstack/react-router';
|
|
12
12
|
import {useAtom} from 'jotai';
|
|
13
13
|
import {useEffect, useRef, useState} from 'react';
|
|
14
14
|
import {EmailCodeVerification} from '#/components/email-code-verification.js';
|
|
@@ -16,6 +16,7 @@ import {useRefreshAuth} from '#hooks/api/refresh-auth.js';
|
|
|
16
16
|
import {useSignupAuth} from '#hooks/api/signup-auth.js';
|
|
17
17
|
import {useResendEmailVerificationAuth, useVerifyEmailAuth} from '#hooks/api/verify-email-auth.js';
|
|
18
18
|
import {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';
|
|
19
|
+
import {validateRedirectSearch} from '../routes/inputs.js';
|
|
19
20
|
import {signupErrorToFormError} from './form-errors.js';
|
|
20
21
|
import {authErrorMessage} from './form-utils.js';
|
|
21
22
|
import {
|
|
@@ -30,7 +31,7 @@ export function SignupPage() {
|
|
|
30
31
|
const resendEmailVerification = useResendEmailVerificationAuth();
|
|
31
32
|
const refreshAuth = useRefreshAuth();
|
|
32
33
|
const navigate = useNavigate();
|
|
33
|
-
const search =
|
|
34
|
+
const search = useRouteSearch(validateRedirectSearch);
|
|
34
35
|
const invitationToken = extractInvitationToken(search.redirect);
|
|
35
36
|
const invitationPreview = useInvitationContext(invitationToken);
|
|
36
37
|
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
@@ -50,13 +51,18 @@ export function SignupPage() {
|
|
|
50
51
|
onSubmit: async ({value}) => {
|
|
51
52
|
setFormError(undefined);
|
|
52
53
|
try {
|
|
53
|
-
const
|
|
54
|
+
const parsed = signupBodySchema.parse({
|
|
54
55
|
email: value.email,
|
|
55
56
|
password: value.password,
|
|
56
57
|
name: value.name,
|
|
57
58
|
...(invitationToken ? {invitation_token: invitationToken} : {}),
|
|
58
59
|
});
|
|
59
|
-
const result = await signup.mutateAsync(
|
|
60
|
+
const result = await signup.mutateAsync({
|
|
61
|
+
email: parsed.email,
|
|
62
|
+
password: parsed.password,
|
|
63
|
+
name: parsed.name,
|
|
64
|
+
...(parsed.invitation_token ? {invitationToken: parsed.invitation_token} : {}),
|
|
65
|
+
});
|
|
60
66
|
skipDraftPersistRef.current = true;
|
|
61
67
|
setAuthFormDraft(initialAuthFormDraft);
|
|
62
68
|
|
|
@@ -132,9 +138,9 @@ export function SignupPage() {
|
|
|
132
138
|
try {
|
|
133
139
|
const result = await resendEmailVerification.mutateAsync({
|
|
134
140
|
email: emailChallenge.email,
|
|
135
|
-
|
|
141
|
+
challengeId: emailChallenge.id,
|
|
136
142
|
});
|
|
137
|
-
setNextResendAvailableAt(result.
|
|
143
|
+
setNextResendAvailableAt(result.nextResendAvailableAt);
|
|
138
144
|
toast.success('If another verification email can be sent, it will arrive shortly.');
|
|
139
145
|
} catch (error) {
|
|
140
146
|
setResendError(authErrorMessage(error));
|
|
@@ -154,7 +160,7 @@ export function SignupPage() {
|
|
|
154
160
|
try {
|
|
155
161
|
await verifyEmail.mutateAsync({
|
|
156
162
|
email: emailChallenge.email,
|
|
157
|
-
|
|
163
|
+
challengeId: emailChallenge.id,
|
|
158
164
|
code,
|
|
159
165
|
});
|
|
160
166
|
await refreshAuth();
|
|
@@ -43,8 +43,8 @@ export function WorkspaceOnboardingPage() {
|
|
|
43
43
|
onSubmit: async ({value}) => {
|
|
44
44
|
setFormError(undefined);
|
|
45
45
|
try {
|
|
46
|
-
const
|
|
47
|
-
const created = await createWorkspace.mutateAsync(
|
|
46
|
+
const command = createWorkspaceBodySchema.parse({name: value.name});
|
|
47
|
+
const created = await createWorkspace.mutateAsync(command);
|
|
48
48
|
toast.success('Workspace created.');
|
|
49
49
|
// Pin the new workspace as the last-active one so a page refresh and
|
|
50
50
|
// future visits to `/` land on it.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {validatePasswordResetSearch, validateRedirectSearch} from './inputs.js';
|
|
2
|
+
|
|
3
|
+
describe('auth route inputs', () => {
|
|
4
|
+
it('keeps only a non-empty redirect', () => {
|
|
5
|
+
expect(validateRedirectSearch({redirect: '/workspaces/w-1'})).toEqual({
|
|
6
|
+
redirect: '/workspaces/w-1',
|
|
7
|
+
});
|
|
8
|
+
expect(validateRedirectSearch({redirect: ''})).toEqual({});
|
|
9
|
+
expect(validateRedirectSearch({redirect: ['unexpected']})).toEqual({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('keeps only a non-empty password reset token', () => {
|
|
13
|
+
expect(validatePasswordResetSearch({token: 'reset-token'})).toEqual({token: 'reset-token'});
|
|
14
|
+
expect(validatePasswordResetSearch({token: ''})).toEqual({});
|
|
15
|
+
expect(validatePasswordResetSearch({token: {value: 'unexpected'}})).toEqual({});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface AuthRedirectSearch {
|
|
2
|
+
redirect?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface PasswordResetSearch {
|
|
6
|
+
token?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function validateRedirectSearch(input: Record<string, unknown>): AuthRedirectSearch {
|
|
10
|
+
const redirect = nonEmptyString(input.redirect);
|
|
11
|
+
return redirect ? {redirect} : {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function validatePasswordResetSearch(input: Record<string, unknown>): PasswordResetSearch {
|
|
15
|
+
const token = nonEmptyString(input.token);
|
|
16
|
+
return token ? {token} : {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
20
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
21
|
+
}
|
package/src/routes/login.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {GuestGuard} from '#components/auth-guard.js';
|
|
3
3
|
import {LoginPage} from '#pages/login-page.js';
|
|
4
|
+
import {validateRedirectSearch} from './inputs.js';
|
|
4
5
|
|
|
5
6
|
export default defineRoute({
|
|
7
|
+
validateSearch: validateRedirectSearch,
|
|
6
8
|
component: () => (
|
|
7
9
|
<GuestGuard>
|
|
8
10
|
<LoginPage />
|
package/src/routes/logout.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {LogoutPage} from '#pages/logout-page.js';
|
|
3
|
+
import {validateRedirectSearch} from './inputs.js';
|
|
3
4
|
|
|
4
|
-
export default defineRoute({component: LogoutPage});
|
|
5
|
+
export default defineRoute({validateSearch: validateRedirectSearch, component: LogoutPage});
|
package/src/routes/reset.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {GuestGuard} from '#components/auth-guard.js';
|
|
3
3
|
import {PasswordResetPage} from '#pages/password-reset-page.js';
|
|
4
|
+
import {validatePasswordResetSearch} from './inputs.js';
|
|
4
5
|
|
|
5
6
|
export default defineRoute({
|
|
7
|
+
validateSearch: validatePasswordResetSearch,
|
|
6
8
|
component: () => (
|
|
7
9
|
<GuestGuard>
|
|
8
10
|
<PasswordResetPage />
|
package/src/routes/signup.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {GuestGuard} from '#components/auth-guard.js';
|
|
3
3
|
import {SignupPage} from '#pages/signup-page.js';
|
|
4
|
+
import {validateRedirectSearch} from './inputs.js';
|
|
4
5
|
|
|
5
6
|
export default defineRoute({
|
|
7
|
+
validateSearch: validateRedirectSearch,
|
|
6
8
|
component: () => (
|
|
7
9
|
<GuestGuard>
|
|
8
10
|
<SignupPage />
|