@djangocfg/api 2.1.542 → 2.1.543
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/dist/auth-server.cjs +1 -0
- package/dist/auth-server.cjs.map +1 -1
- package/dist/auth.cjs +57 -59
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +134 -7
- package/dist/auth.d.ts +134 -7
- package/dist/auth.mjs +69 -72
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +1 -0
- package/dist/clients.cjs.map +1 -1
- package/dist/hooks.cjs +1 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/package.json +3 -2
- package/src/auth/__tests__/useTwoFactorStatus.test.ts +2 -2
- package/src/auth/context/AccountsContext.tsx +15 -11
- package/src/auth/hooks/useAuthForm.ts +0 -1
- package/src/auth/hooks/useCfgRouter.ts +1 -4
- package/src/auth/hooks/useGithubAuth.ts +25 -3
- package/src/auth/hooks/useLocalStorage.ts +7 -30
- package/src/auth/hooks/useSessionStorage.ts +6 -29
- package/src/auth/hooks/useTwoFactorVerify.ts +5 -9
- package/src/auth/utils/analytics.ts +22 -2
- package/src/auth/utils/errors.ts +22 -11
- package/src/auth/utils/storage.ts +64 -0
package/dist/auth.d.cts
CHANGED
|
@@ -39,6 +39,59 @@ type CfgUserUpdateRequest = {
|
|
|
39
39
|
*/
|
|
40
40
|
timezone?: string;
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Response with JWT tokens after OAuth authentication.
|
|
44
|
+
*
|
|
45
|
+
* When 2FA is required:
|
|
46
|
+
* - requires_2fa: True
|
|
47
|
+
* - session_id: UUID of 2FA verification session
|
|
48
|
+
* - access/refresh/user: null
|
|
49
|
+
*
|
|
50
|
+
* When 2FA is not required:
|
|
51
|
+
* - requires_2fa: False
|
|
52
|
+
* - session_id: null
|
|
53
|
+
* - access/refresh/user: populated
|
|
54
|
+
*/
|
|
55
|
+
type OAuthTokenResponse = {
|
|
56
|
+
/**
|
|
57
|
+
* True if 2FA verification is required
|
|
58
|
+
*/
|
|
59
|
+
requires_2fa?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* 2FA session ID (only when requires_2fa=True)
|
|
62
|
+
*/
|
|
63
|
+
session_id?: string | null;
|
|
64
|
+
/**
|
|
65
|
+
* JWT access token (null when requires_2fa=True)
|
|
66
|
+
*/
|
|
67
|
+
access?: string | null;
|
|
68
|
+
/**
|
|
69
|
+
* JWT refresh token (null when requires_2fa=True)
|
|
70
|
+
*/
|
|
71
|
+
refresh?: string | null;
|
|
72
|
+
/**
|
|
73
|
+
* Authenticated user info (null when requires_2fa=True)
|
|
74
|
+
*/
|
|
75
|
+
user?: {
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
} | null;
|
|
78
|
+
/**
|
|
79
|
+
* True if a new user was created during this OAuth flow
|
|
80
|
+
*/
|
|
81
|
+
is_new_user?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* True if a new OAuth connection was created
|
|
84
|
+
*/
|
|
85
|
+
is_new_connection?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* True if user should be prompted to enable 2FA
|
|
88
|
+
*/
|
|
89
|
+
should_prompt_2fa?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether this login should persist across browser restarts
|
|
92
|
+
*/
|
|
93
|
+
persistent_session?: boolean;
|
|
94
|
+
};
|
|
42
95
|
/**
|
|
43
96
|
* Serializer for OTP request.
|
|
44
97
|
*/
|
|
@@ -149,6 +202,49 @@ type PatchedCfgUserUpdateRequest = {
|
|
|
149
202
|
*/
|
|
150
203
|
timezone?: string;
|
|
151
204
|
};
|
|
205
|
+
/**
|
|
206
|
+
* User data returned after 2FA verification.
|
|
207
|
+
*/
|
|
208
|
+
type TotpVerifyUser = {
|
|
209
|
+
readonly id: number;
|
|
210
|
+
readonly email: string;
|
|
211
|
+
first_name?: string | null;
|
|
212
|
+
last_name?: string | null;
|
|
213
|
+
/**
|
|
214
|
+
* Get user's full name.
|
|
215
|
+
*/
|
|
216
|
+
readonly full_name: string;
|
|
217
|
+
/**
|
|
218
|
+
* Get user's initials for avatar fallback.
|
|
219
|
+
*/
|
|
220
|
+
readonly initials: string;
|
|
221
|
+
/**
|
|
222
|
+
* Get formatted username for display.
|
|
223
|
+
*/
|
|
224
|
+
readonly display_username: string;
|
|
225
|
+
company?: string | null;
|
|
226
|
+
phone?: string | null;
|
|
227
|
+
position?: string | null;
|
|
228
|
+
language?: string | null;
|
|
229
|
+
timezone?: string | null;
|
|
230
|
+
readonly avatar: string | null;
|
|
231
|
+
/**
|
|
232
|
+
* Staff status
|
|
233
|
+
*
|
|
234
|
+
* Designates whether the user can log into this admin site.
|
|
235
|
+
*/
|
|
236
|
+
readonly is_staff: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* Superuser status
|
|
239
|
+
*
|
|
240
|
+
* Designates that this user has all permissions without explicitly assigning them.
|
|
241
|
+
*/
|
|
242
|
+
readonly is_superuser: boolean;
|
|
243
|
+
readonly date_joined: string;
|
|
244
|
+
readonly last_login: string | null;
|
|
245
|
+
readonly unanswered_messages_count: number;
|
|
246
|
+
readonly api_key: string | null;
|
|
247
|
+
};
|
|
152
248
|
/**
|
|
153
249
|
* Serializer for user details.
|
|
154
250
|
*/
|
|
@@ -522,7 +618,7 @@ interface AuthProviderProps {
|
|
|
522
618
|
enabled?: boolean;
|
|
523
619
|
}
|
|
524
620
|
|
|
525
|
-
declare const AuthContext: React__default.Context<AuthContextType>;
|
|
621
|
+
declare const AuthContext: React__default.Context<AuthContextType | undefined>;
|
|
526
622
|
/**
|
|
527
623
|
* AuthProvider — wraps AccountsProvider + AuthProviderInternal.
|
|
528
624
|
*
|
|
@@ -683,11 +779,25 @@ declare const useAutoAuth: (options?: UseAutoAuthOptions) => {
|
|
|
683
779
|
isAllowedPath: boolean;
|
|
684
780
|
};
|
|
685
781
|
|
|
782
|
+
/**
|
|
783
|
+
* The user payload as the OAuth endpoint declares it.
|
|
784
|
+
*
|
|
785
|
+
* Derived from the generated response rather than restated, so a schema change
|
|
786
|
+
* reaches callers through `make gen` instead of drifting silently. Today the
|
|
787
|
+
* backend types it as a free-form object; when it names a real user model, this
|
|
788
|
+
* narrows on its own.
|
|
789
|
+
*/
|
|
790
|
+
type OAuthUser = NonNullable<OAuthTokenResponse['user']>;
|
|
686
791
|
interface UseGithubAuthOptions {
|
|
687
792
|
sourceUrl?: string;
|
|
688
793
|
/** Keep the completed browser session for 30 days. */
|
|
689
794
|
rememberMe?: boolean;
|
|
690
|
-
|
|
795
|
+
/**
|
|
796
|
+
* The OAuth endpoint's OpenAPI schema types `user` as a free-form object, so
|
|
797
|
+
* this is what the server actually promises — not a `User`. Narrow it at the
|
|
798
|
+
* call site; typing it tighter here would be an assertion, not a fact.
|
|
799
|
+
*/
|
|
800
|
+
onSuccess?: (user: OAuthUser, isNewUser: boolean) => void;
|
|
691
801
|
onError?: (error: string) => void;
|
|
692
802
|
/** Callback when 2FA is required */
|
|
693
803
|
onRequires2FA?: (sessionId: string, shouldPrompt2FA: boolean) => void;
|
|
@@ -724,7 +834,7 @@ declare const useGithubAuth: (options?: UseGithubAuthOptions) => UseGithubAuthRe
|
|
|
724
834
|
|
|
725
835
|
interface UseTwoFactorVerifyOptions {
|
|
726
836
|
/** Callback on successful 2FA verification */
|
|
727
|
-
onSuccess?: (user:
|
|
837
|
+
onSuccess?: (user: TotpVerifyUser) => void;
|
|
728
838
|
/** Callback on error */
|
|
729
839
|
onError?: (error: string) => void;
|
|
730
840
|
/** URL to redirect after successful verification */
|
|
@@ -1014,9 +1124,14 @@ declare const useDeleteAccount: () => UseDeleteAccountReturn;
|
|
|
1014
1124
|
declare const validateEmail: (email: string) => boolean;
|
|
1015
1125
|
|
|
1016
1126
|
/**
|
|
1017
|
-
* Format authentication error for display
|
|
1127
|
+
* Format authentication error for display.
|
|
1128
|
+
*
|
|
1129
|
+
* Each candidate is checked to be a string before it is returned. Under the
|
|
1130
|
+
* previous `any` the check was only that the property was truthy, so a DRF
|
|
1131
|
+
* field-error object (`{detail: {email: [...]}}`) reached the UI as
|
|
1132
|
+
* "[object Object]" — an error message that tells the user nothing.
|
|
1018
1133
|
*/
|
|
1019
|
-
declare const formatAuthError: (error:
|
|
1134
|
+
declare const formatAuthError: (error: unknown) => string;
|
|
1020
1135
|
|
|
1021
1136
|
/**
|
|
1022
1137
|
* Pure auth-guard state resolution.
|
|
@@ -1114,15 +1229,27 @@ declare enum AnalyticsCategory {
|
|
|
1114
1229
|
}
|
|
1115
1230
|
type AnalyticsEventType = `${AnalyticsEvent}`;
|
|
1116
1231
|
type AnalyticsCategoryType = `${AnalyticsCategory}`;
|
|
1232
|
+
/**
|
|
1233
|
+
* What an analytics backend can actually carry.
|
|
1234
|
+
*
|
|
1235
|
+
* GA4 and OpenPanel serialise a property to a scalar; an object or a function
|
|
1236
|
+
* arrives as "[object Object]" without erroring anywhere. Declaring the wider
|
|
1237
|
+
* `unknown` here promised a forwarding guarantee no implementation keeps, and
|
|
1238
|
+
* it made this sink incompatible with `AnalyticsEventProperties` in
|
|
1239
|
+
* `@djangocfg/analytics` — the two describe the same channel and must agree.
|
|
1240
|
+
* Kept as a local declaration rather than an import: `api` does not depend on
|
|
1241
|
+
* `analytics`, and one shared four-member union is not worth that edge.
|
|
1242
|
+
*/
|
|
1243
|
+
type AnalyticsPropertyValue = string | number | boolean | null;
|
|
1117
1244
|
interface AnalyticsEventParams {
|
|
1118
1245
|
category?: AnalyticsCategoryType;
|
|
1119
1246
|
label?: string;
|
|
1120
1247
|
value?: number;
|
|
1121
|
-
[key: string]:
|
|
1248
|
+
[key: string]: AnalyticsPropertyValue | undefined;
|
|
1122
1249
|
}
|
|
1123
1250
|
/** What an analytics implementation must provide to receive auth events. */
|
|
1124
1251
|
interface AnalyticsSink {
|
|
1125
|
-
event(name: string, params?: Record<string,
|
|
1252
|
+
event(name: string, params?: Record<string, AnalyticsPropertyValue | undefined>): void;
|
|
1126
1253
|
setUser(userId: string | null): void;
|
|
1127
1254
|
}
|
|
1128
1255
|
/**
|
package/dist/auth.d.ts
CHANGED
|
@@ -39,6 +39,59 @@ type CfgUserUpdateRequest = {
|
|
|
39
39
|
*/
|
|
40
40
|
timezone?: string;
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Response with JWT tokens after OAuth authentication.
|
|
44
|
+
*
|
|
45
|
+
* When 2FA is required:
|
|
46
|
+
* - requires_2fa: True
|
|
47
|
+
* - session_id: UUID of 2FA verification session
|
|
48
|
+
* - access/refresh/user: null
|
|
49
|
+
*
|
|
50
|
+
* When 2FA is not required:
|
|
51
|
+
* - requires_2fa: False
|
|
52
|
+
* - session_id: null
|
|
53
|
+
* - access/refresh/user: populated
|
|
54
|
+
*/
|
|
55
|
+
type OAuthTokenResponse = {
|
|
56
|
+
/**
|
|
57
|
+
* True if 2FA verification is required
|
|
58
|
+
*/
|
|
59
|
+
requires_2fa?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* 2FA session ID (only when requires_2fa=True)
|
|
62
|
+
*/
|
|
63
|
+
session_id?: string | null;
|
|
64
|
+
/**
|
|
65
|
+
* JWT access token (null when requires_2fa=True)
|
|
66
|
+
*/
|
|
67
|
+
access?: string | null;
|
|
68
|
+
/**
|
|
69
|
+
* JWT refresh token (null when requires_2fa=True)
|
|
70
|
+
*/
|
|
71
|
+
refresh?: string | null;
|
|
72
|
+
/**
|
|
73
|
+
* Authenticated user info (null when requires_2fa=True)
|
|
74
|
+
*/
|
|
75
|
+
user?: {
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
} | null;
|
|
78
|
+
/**
|
|
79
|
+
* True if a new user was created during this OAuth flow
|
|
80
|
+
*/
|
|
81
|
+
is_new_user?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* True if a new OAuth connection was created
|
|
84
|
+
*/
|
|
85
|
+
is_new_connection?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* True if user should be prompted to enable 2FA
|
|
88
|
+
*/
|
|
89
|
+
should_prompt_2fa?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether this login should persist across browser restarts
|
|
92
|
+
*/
|
|
93
|
+
persistent_session?: boolean;
|
|
94
|
+
};
|
|
42
95
|
/**
|
|
43
96
|
* Serializer for OTP request.
|
|
44
97
|
*/
|
|
@@ -149,6 +202,49 @@ type PatchedCfgUserUpdateRequest = {
|
|
|
149
202
|
*/
|
|
150
203
|
timezone?: string;
|
|
151
204
|
};
|
|
205
|
+
/**
|
|
206
|
+
* User data returned after 2FA verification.
|
|
207
|
+
*/
|
|
208
|
+
type TotpVerifyUser = {
|
|
209
|
+
readonly id: number;
|
|
210
|
+
readonly email: string;
|
|
211
|
+
first_name?: string | null;
|
|
212
|
+
last_name?: string | null;
|
|
213
|
+
/**
|
|
214
|
+
* Get user's full name.
|
|
215
|
+
*/
|
|
216
|
+
readonly full_name: string;
|
|
217
|
+
/**
|
|
218
|
+
* Get user's initials for avatar fallback.
|
|
219
|
+
*/
|
|
220
|
+
readonly initials: string;
|
|
221
|
+
/**
|
|
222
|
+
* Get formatted username for display.
|
|
223
|
+
*/
|
|
224
|
+
readonly display_username: string;
|
|
225
|
+
company?: string | null;
|
|
226
|
+
phone?: string | null;
|
|
227
|
+
position?: string | null;
|
|
228
|
+
language?: string | null;
|
|
229
|
+
timezone?: string | null;
|
|
230
|
+
readonly avatar: string | null;
|
|
231
|
+
/**
|
|
232
|
+
* Staff status
|
|
233
|
+
*
|
|
234
|
+
* Designates whether the user can log into this admin site.
|
|
235
|
+
*/
|
|
236
|
+
readonly is_staff: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* Superuser status
|
|
239
|
+
*
|
|
240
|
+
* Designates that this user has all permissions without explicitly assigning them.
|
|
241
|
+
*/
|
|
242
|
+
readonly is_superuser: boolean;
|
|
243
|
+
readonly date_joined: string;
|
|
244
|
+
readonly last_login: string | null;
|
|
245
|
+
readonly unanswered_messages_count: number;
|
|
246
|
+
readonly api_key: string | null;
|
|
247
|
+
};
|
|
152
248
|
/**
|
|
153
249
|
* Serializer for user details.
|
|
154
250
|
*/
|
|
@@ -522,7 +618,7 @@ interface AuthProviderProps {
|
|
|
522
618
|
enabled?: boolean;
|
|
523
619
|
}
|
|
524
620
|
|
|
525
|
-
declare const AuthContext: React__default.Context<AuthContextType>;
|
|
621
|
+
declare const AuthContext: React__default.Context<AuthContextType | undefined>;
|
|
526
622
|
/**
|
|
527
623
|
* AuthProvider — wraps AccountsProvider + AuthProviderInternal.
|
|
528
624
|
*
|
|
@@ -683,11 +779,25 @@ declare const useAutoAuth: (options?: UseAutoAuthOptions) => {
|
|
|
683
779
|
isAllowedPath: boolean;
|
|
684
780
|
};
|
|
685
781
|
|
|
782
|
+
/**
|
|
783
|
+
* The user payload as the OAuth endpoint declares it.
|
|
784
|
+
*
|
|
785
|
+
* Derived from the generated response rather than restated, so a schema change
|
|
786
|
+
* reaches callers through `make gen` instead of drifting silently. Today the
|
|
787
|
+
* backend types it as a free-form object; when it names a real user model, this
|
|
788
|
+
* narrows on its own.
|
|
789
|
+
*/
|
|
790
|
+
type OAuthUser = NonNullable<OAuthTokenResponse['user']>;
|
|
686
791
|
interface UseGithubAuthOptions {
|
|
687
792
|
sourceUrl?: string;
|
|
688
793
|
/** Keep the completed browser session for 30 days. */
|
|
689
794
|
rememberMe?: boolean;
|
|
690
|
-
|
|
795
|
+
/**
|
|
796
|
+
* The OAuth endpoint's OpenAPI schema types `user` as a free-form object, so
|
|
797
|
+
* this is what the server actually promises — not a `User`. Narrow it at the
|
|
798
|
+
* call site; typing it tighter here would be an assertion, not a fact.
|
|
799
|
+
*/
|
|
800
|
+
onSuccess?: (user: OAuthUser, isNewUser: boolean) => void;
|
|
691
801
|
onError?: (error: string) => void;
|
|
692
802
|
/** Callback when 2FA is required */
|
|
693
803
|
onRequires2FA?: (sessionId: string, shouldPrompt2FA: boolean) => void;
|
|
@@ -724,7 +834,7 @@ declare const useGithubAuth: (options?: UseGithubAuthOptions) => UseGithubAuthRe
|
|
|
724
834
|
|
|
725
835
|
interface UseTwoFactorVerifyOptions {
|
|
726
836
|
/** Callback on successful 2FA verification */
|
|
727
|
-
onSuccess?: (user:
|
|
837
|
+
onSuccess?: (user: TotpVerifyUser) => void;
|
|
728
838
|
/** Callback on error */
|
|
729
839
|
onError?: (error: string) => void;
|
|
730
840
|
/** URL to redirect after successful verification */
|
|
@@ -1014,9 +1124,14 @@ declare const useDeleteAccount: () => UseDeleteAccountReturn;
|
|
|
1014
1124
|
declare const validateEmail: (email: string) => boolean;
|
|
1015
1125
|
|
|
1016
1126
|
/**
|
|
1017
|
-
* Format authentication error for display
|
|
1127
|
+
* Format authentication error for display.
|
|
1128
|
+
*
|
|
1129
|
+
* Each candidate is checked to be a string before it is returned. Under the
|
|
1130
|
+
* previous `any` the check was only that the property was truthy, so a DRF
|
|
1131
|
+
* field-error object (`{detail: {email: [...]}}`) reached the UI as
|
|
1132
|
+
* "[object Object]" — an error message that tells the user nothing.
|
|
1018
1133
|
*/
|
|
1019
|
-
declare const formatAuthError: (error:
|
|
1134
|
+
declare const formatAuthError: (error: unknown) => string;
|
|
1020
1135
|
|
|
1021
1136
|
/**
|
|
1022
1137
|
* Pure auth-guard state resolution.
|
|
@@ -1114,15 +1229,27 @@ declare enum AnalyticsCategory {
|
|
|
1114
1229
|
}
|
|
1115
1230
|
type AnalyticsEventType = `${AnalyticsEvent}`;
|
|
1116
1231
|
type AnalyticsCategoryType = `${AnalyticsCategory}`;
|
|
1232
|
+
/**
|
|
1233
|
+
* What an analytics backend can actually carry.
|
|
1234
|
+
*
|
|
1235
|
+
* GA4 and OpenPanel serialise a property to a scalar; an object or a function
|
|
1236
|
+
* arrives as "[object Object]" without erroring anywhere. Declaring the wider
|
|
1237
|
+
* `unknown` here promised a forwarding guarantee no implementation keeps, and
|
|
1238
|
+
* it made this sink incompatible with `AnalyticsEventProperties` in
|
|
1239
|
+
* `@djangocfg/analytics` — the two describe the same channel and must agree.
|
|
1240
|
+
* Kept as a local declaration rather than an import: `api` does not depend on
|
|
1241
|
+
* `analytics`, and one shared four-member union is not worth that edge.
|
|
1242
|
+
*/
|
|
1243
|
+
type AnalyticsPropertyValue = string | number | boolean | null;
|
|
1117
1244
|
interface AnalyticsEventParams {
|
|
1118
1245
|
category?: AnalyticsCategoryType;
|
|
1119
1246
|
label?: string;
|
|
1120
1247
|
value?: number;
|
|
1121
|
-
[key: string]:
|
|
1248
|
+
[key: string]: AnalyticsPropertyValue | undefined;
|
|
1122
1249
|
}
|
|
1123
1250
|
/** What an analytics implementation must provide to receive auth events. */
|
|
1124
1251
|
interface AnalyticsSink {
|
|
1125
|
-
event(name: string, params?: Record<string,
|
|
1252
|
+
event(name: string, params?: Record<string, AnalyticsPropertyValue | undefined>): void;
|
|
1126
1253
|
setUser(userId: string | null): void;
|
|
1127
1254
|
}
|
|
1128
1255
|
/**
|