@object-ui/auth 3.3.0 → 3.3.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/CHANGELOG.md +107 -0
- package/README.md +21 -1
- package/dist/AuthContext.d.ts +58 -1
- package/dist/AuthContext.d.ts.map +1 -1
- package/dist/AuthProvider.d.ts.map +1 -1
- package/dist/AuthProvider.js +153 -1
- package/dist/LoginForm.d.ts.map +1 -1
- package/dist/LoginForm.js +2 -1
- package/dist/RegisterForm.d.ts.map +1 -1
- package/dist/RegisterForm.js +2 -1
- package/dist/SocialSignInButtons.d.ts +26 -0
- package/dist/SocialSignInButtons.d.ts.map +1 -0
- package/dist/SocialSignInButtons.js +97 -0
- package/dist/createAuthClient.d.ts +16 -4
- package/dist/createAuthClient.d.ts.map +1 -1
- package/dist/createAuthClient.js +256 -5
- package/dist/createAuthenticatedFetch.d.ts +15 -11
- package/dist/createAuthenticatedFetch.d.ts.map +1 -1
- package/dist/createAuthenticatedFetch.js +52 -11
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/useAuth.d.ts.map +1 -1
- package/dist/useAuth.js +21 -0
- package/package.json +32 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @object-ui/auth
|
|
2
|
+
|
|
3
|
+
## 3.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @object-ui/types@3.3.1
|
|
8
|
+
|
|
9
|
+
## 3.3.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- @object-ui/types@3.3.0
|
|
14
|
+
|
|
15
|
+
## 3.2.0
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- @object-ui/types@3.2.0
|
|
20
|
+
|
|
21
|
+
## 3.1.5
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- @object-ui/types@3.1.5
|
|
26
|
+
|
|
27
|
+
## 3.1.4
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- @object-ui/types@3.1.4
|
|
32
|
+
|
|
33
|
+
## 3.1.3
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- @object-ui/types@3.1.3
|
|
38
|
+
|
|
39
|
+
## 3.1.2
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- @object-ui/types@3.1.2
|
|
44
|
+
|
|
45
|
+
## 3.1.1
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- Updated dependencies
|
|
50
|
+
- @object-ui/types@3.1.1
|
|
51
|
+
|
|
52
|
+
## 3.0.3
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- @object-ui/types@3.0.3
|
|
57
|
+
|
|
58
|
+
## 3.0.2
|
|
59
|
+
|
|
60
|
+
### Patch Changes
|
|
61
|
+
|
|
62
|
+
- @object-ui/types@3.0.2
|
|
63
|
+
|
|
64
|
+
## 3.0.1
|
|
65
|
+
|
|
66
|
+
### Patch Changes
|
|
67
|
+
|
|
68
|
+
- @object-ui/types@3.0.1
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **Preview Mode** (`previewMode` prop on `AuthProvider`): Auto-login with simulated identity for marketplace demos and app showcases. Configurable role, display name, session expiry, read-only mode, and banner message.
|
|
73
|
+
- **PreviewBanner** component: Renders a status banner when preview mode is active.
|
|
74
|
+
- `isPreviewMode` and `previewMode` fields exposed on `AuthContextValue` / `useAuth()` hook.
|
|
75
|
+
- New `PreviewModeOptions` type mirroring spec's `PreviewModeConfig`.
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
- Upgraded `@objectstack/spec` from `^3.0.2` to `^3.0.4`.
|
|
80
|
+
|
|
81
|
+
## 3.0.0
|
|
82
|
+
|
|
83
|
+
### Minor Changes
|
|
84
|
+
|
|
85
|
+
- 87979c3: Upgrade to @objectstack v3.0.0 and console bundle optimization
|
|
86
|
+
- Upgraded all @objectstack/\* packages from ^2.0.7 to ^3.0.0
|
|
87
|
+
- Breaking change migrations: Hub → Cloud namespace, definePlugin removed, PaginatedResult.value → .records, PaginatedResult.count → .total, client.meta.getObject() → client.meta.getItem()
|
|
88
|
+
- Console bundle optimization: split monolithic 3.7 MB chunk into 17 granular cacheable chunks (95% main entry reduction)
|
|
89
|
+
- Added gzip + brotli pre-compression via vite-plugin-compression2
|
|
90
|
+
- Lazy MSW loading for build:server (~150 KB gzip saved)
|
|
91
|
+
- Added bundle analysis with rollup-plugin-visualizer
|
|
92
|
+
|
|
93
|
+
### Patch Changes
|
|
94
|
+
|
|
95
|
+
- Updated dependencies [87979c3]
|
|
96
|
+
- @object-ui/types@3.0.0
|
|
97
|
+
|
|
98
|
+
## 2.0.0
|
|
99
|
+
|
|
100
|
+
### Major Changes
|
|
101
|
+
|
|
102
|
+
- b859617: Release v1.0.0 — unify all package versions to 1.0.0
|
|
103
|
+
|
|
104
|
+
### Patch Changes
|
|
105
|
+
|
|
106
|
+
- Updated dependencies [b859617]
|
|
107
|
+
- @object-ui/types@2.0.0
|
package/README.md
CHANGED
|
@@ -201,6 +201,26 @@ function MyComponent() {
|
|
|
201
201
|
|
|
202
202
|
> **⚠️ Security:** Preview mode should **never** be used in production environments.
|
|
203
203
|
|
|
204
|
+
<!-- release-metadata:v3.3.0 -->
|
|
205
|
+
|
|
206
|
+
## Compatibility
|
|
207
|
+
|
|
208
|
+
- **React:** 18.x or 19.x
|
|
209
|
+
- **Node.js:** ≥ 18
|
|
210
|
+
- **TypeScript:** ≥ 5.0 (strict mode)
|
|
211
|
+
- **`@objectstack/spec`:** ^3.3.0
|
|
212
|
+
- **`@objectstack/client`:** ^3.3.0
|
|
213
|
+
- **Tailwind CSS:** ≥ 3.4 (for packages with UI)
|
|
214
|
+
|
|
215
|
+
## Links
|
|
216
|
+
|
|
217
|
+
- 📚 [Documentation](https://www.objectui.org/docs/packages/auth)
|
|
218
|
+
- 📦 [npm package](https://www.npmjs.com/package/@object-ui/auth)
|
|
219
|
+
- 📝 [Changelog](./CHANGELOG.md)
|
|
220
|
+
- 🐛 [Report an issue](https://github.com/objectstack-ai/objectui/issues)
|
|
221
|
+
- 🤝 [Contributing Guide](https://github.com/objectstack-ai/objectui/blob/main/CONTRIBUTING.md)
|
|
222
|
+
- 🗺️ [Roadmap](https://github.com/objectstack-ai/objectui/blob/main/ROADMAP.md)
|
|
223
|
+
|
|
204
224
|
## License
|
|
205
225
|
|
|
206
|
-
MIT
|
|
226
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/AuthContext.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type { AuthUser, AuthSession, PreviewModeOptions } from './types';
|
|
8
|
+
import type { AuthUser, AuthSession, PreviewModeOptions, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthPublicConfig, SignInWithProviderOptions } from './types';
|
|
9
9
|
export interface AuthContextValue {
|
|
10
10
|
/** Current authenticated user */
|
|
11
11
|
user: AuthUser | null;
|
|
@@ -33,6 +33,63 @@ export interface AuthContextValue {
|
|
|
33
33
|
forgotPassword: (email: string) => Promise<void>;
|
|
34
34
|
/** Reset password with token */
|
|
35
35
|
resetPassword: (token: string, newPassword: string) => Promise<void>;
|
|
36
|
+
/** Fetch the public auth configuration (providers, features) */
|
|
37
|
+
getAuthConfig: () => Promise<AuthPublicConfig>;
|
|
38
|
+
/** Initiate sign-in with a third-party provider (Google, GitHub, OIDC, etc.) */
|
|
39
|
+
signInWithProvider: (providerId: string, options?: SignInWithProviderOptions) => Promise<void>;
|
|
40
|
+
/** All organizations the user belongs to */
|
|
41
|
+
organizations: AuthOrganization[];
|
|
42
|
+
/** Currently active organization */
|
|
43
|
+
activeOrganization: AuthOrganization | null;
|
|
44
|
+
/** Whether organizations are loading */
|
|
45
|
+
isOrganizationsLoading: boolean;
|
|
46
|
+
/** Switch the active organization (workspace) */
|
|
47
|
+
switchOrganization: (orgId: string) => Promise<void>;
|
|
48
|
+
/** Create a new organization */
|
|
49
|
+
createOrganization: (data: {
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
logo?: string;
|
|
53
|
+
}) => Promise<AuthOrganization>;
|
|
54
|
+
/** Refresh the organizations list */
|
|
55
|
+
refreshOrganizations: () => Promise<void>;
|
|
56
|
+
/** Update organization details (owner/admin) */
|
|
57
|
+
updateOrganization: (orgId: string, data: Partial<Pick<AuthOrganization, 'name' | 'slug' | 'logo' | 'metadata'>>) => Promise<AuthOrganization>;
|
|
58
|
+
/** Delete an organization (owner) */
|
|
59
|
+
deleteOrganization: (orgId: string) => Promise<void>;
|
|
60
|
+
/** Current user leaves the given organization */
|
|
61
|
+
leaveOrganization: (orgId: string) => Promise<void>;
|
|
62
|
+
/** List members of an organization */
|
|
63
|
+
getMembers: (orgId: string) => Promise<AuthOrganizationMember[]>;
|
|
64
|
+
/** Invite a user by email */
|
|
65
|
+
inviteMember: (data: {
|
|
66
|
+
organizationId: string;
|
|
67
|
+
email: string;
|
|
68
|
+
role: string;
|
|
69
|
+
}) => Promise<AuthInvitation>;
|
|
70
|
+
/** Remove a member by id */
|
|
71
|
+
removeMember: (data: {
|
|
72
|
+
organizationId: string;
|
|
73
|
+
memberIdOrUserId: string;
|
|
74
|
+
}) => Promise<void>;
|
|
75
|
+
/** Update a member's role */
|
|
76
|
+
updateMemberRole: (data: {
|
|
77
|
+
organizationId: string;
|
|
78
|
+
memberId: string;
|
|
79
|
+
role: string;
|
|
80
|
+
}) => Promise<void>;
|
|
81
|
+
/** List pending invitations for an organization */
|
|
82
|
+
listInvitations: (orgId: string) => Promise<AuthInvitation[]>;
|
|
83
|
+
/** Cancel an invitation */
|
|
84
|
+
cancelInvitation: (invitationId: string) => Promise<void>;
|
|
85
|
+
/** Get invitation details by id */
|
|
86
|
+
getInvitation: (invitationId: string) => Promise<AuthInvitation>;
|
|
87
|
+
/** Accept an invitation as the current user */
|
|
88
|
+
acceptInvitation: (invitationId: string) => Promise<void>;
|
|
89
|
+
/** Reject an invitation as the current user */
|
|
90
|
+
rejectInvitation: (invitationId: string) => Promise<void>;
|
|
91
|
+
/** List invitations addressed to the current user */
|
|
92
|
+
listUserInvitations: () => Promise<AuthInvitation[]>;
|
|
36
93
|
}
|
|
37
94
|
export declare const AuthCtx: import("react").Context<AuthContextValue | null>;
|
|
38
95
|
//# sourceMappingURL=AuthContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../src/AuthContext.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../src/AuthContext.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,cAAc,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEhL,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,wCAAwC;IACxC,eAAe,EAAE,OAAO,CAAC;IACzB,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,2BAA2B;IAC3B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,iDAAiD;IACjD,aAAa,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACvC,sCAAsC;IACtC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,6CAA6C;IAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,gCAAgC;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,0BAA0B;IAC1B,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,6BAA6B;IAC7B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,gCAAgC;IAChC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,gEAAgE;IAChE,aAAa,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/C,gFAAgF;IAChF,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAI/F,4CAA4C;IAC5C,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,oCAAoC;IACpC,kBAAkB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,wCAAwC;IACxC,sBAAsB,EAAE,OAAO,CAAC;IAChC,iDAAiD;IACjD,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,gCAAgC;IAChC,kBAAkB,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvG,qCAAqC;IACrC,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,gDAAgD;IAChD,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC,CAAC,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/I,qCAAqC;IACrC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iDAAiD;IACjD,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAIpD,sCAAsC;IACtC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IACjE,6BAA6B;IAC7B,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACzG,4BAA4B;IAC5B,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,6BAA6B;IAC7B,gBAAgB,EAAE,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAItG,mDAAmD;IACnD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC9D,2BAA2B;IAC3B,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mCAAmC;IACnC,aAAa,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACjE,+CAA+C;IAC/C,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,+CAA+C;IAC/C,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,qDAAqD;IACrD,mBAAmB,EAAE,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CACtD;AAED,eAAO,MAAM,OAAO,kDAA+C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAwB,kBAAkB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAwB,kBAAkB,EAAE,kBAAkB,EAAyG,MAAM,SAAS,CAAC;AAKnM,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EAAE,cAAc,EACtB,iBAAiB,EACjB,OAAc,EACd,WAAW,EACX,QAAQ,GACT,EAAE,iBAAiB,2CA4anB"}
|
package/dist/AuthProvider.js
CHANGED
|
@@ -9,6 +9,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
10
10
|
import { AuthCtx } from './AuthContext';
|
|
11
11
|
import { createAuthClient } from './createAuthClient';
|
|
12
|
+
import { ActiveOrganizationStorage } from './createAuthenticatedFetch';
|
|
12
13
|
/**
|
|
13
14
|
* Authentication context provider.
|
|
14
15
|
*
|
|
@@ -41,6 +42,10 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
41
42
|
const [session, setSession] = useState(null);
|
|
42
43
|
const [isLoading, setIsLoading] = useState(true);
|
|
43
44
|
const [error, setError] = useState(null);
|
|
45
|
+
// Organization / workspace state
|
|
46
|
+
const [organizations, setOrganizations] = useState([]);
|
|
47
|
+
const [activeOrganization, setActiveOrganization] = useState(null);
|
|
48
|
+
const [isOrganizationsLoading, setIsOrganizationsLoading] = useState(false);
|
|
44
49
|
// Determine if we're in preview mode
|
|
45
50
|
const isPreviewMode = previewMode != null;
|
|
46
51
|
// If auth is disabled or in preview mode, automatically set as authenticated
|
|
@@ -204,6 +209,125 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
204
209
|
throw authError;
|
|
205
210
|
}
|
|
206
211
|
}, [client]);
|
|
212
|
+
const getAuthConfig = useCallback(() => client.getConfig(), [client]);
|
|
213
|
+
const signInWithProvider = useCallback(async (providerId, options) => {
|
|
214
|
+
setError(null);
|
|
215
|
+
try {
|
|
216
|
+
await client.signInWithProvider(providerId, options);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
const authError = err instanceof Error ? err : new Error(String(err));
|
|
220
|
+
setError(authError);
|
|
221
|
+
throw authError;
|
|
222
|
+
}
|
|
223
|
+
}, [client]);
|
|
224
|
+
// --- Organization methods ---
|
|
225
|
+
const refreshOrganizations = useCallback(async () => {
|
|
226
|
+
if (!enabled || isPreviewMode)
|
|
227
|
+
return;
|
|
228
|
+
setIsOrganizationsLoading(true);
|
|
229
|
+
try {
|
|
230
|
+
const orgs = await client.listOrganizations();
|
|
231
|
+
setOrganizations(orgs);
|
|
232
|
+
// If no active org is set but orgs exist, try to get active from server
|
|
233
|
+
if (orgs.length > 0 && !activeOrganization) {
|
|
234
|
+
try {
|
|
235
|
+
const active = await client.getActiveOrganization();
|
|
236
|
+
if (active) {
|
|
237
|
+
setActiveOrganization(active);
|
|
238
|
+
ActiveOrganizationStorage.set(active.id);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// No active org set — that's fine
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
console.warn('[AuthProvider] Failed to load organizations:', err);
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
setIsOrganizationsLoading(false);
|
|
251
|
+
}
|
|
252
|
+
}, [client, enabled, isPreviewMode, activeOrganization]);
|
|
253
|
+
// Load organizations once user is authenticated
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
if (user && enabled && !isPreviewMode) {
|
|
256
|
+
refreshOrganizations();
|
|
257
|
+
}
|
|
258
|
+
}, [user, enabled, isPreviewMode]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
259
|
+
const switchOrganization = useCallback(async (orgId) => {
|
|
260
|
+
setError(null);
|
|
261
|
+
try {
|
|
262
|
+
const org = await client.setActiveOrganization(orgId);
|
|
263
|
+
setActiveOrganization(org);
|
|
264
|
+
// Persist for header injection
|
|
265
|
+
if (org) {
|
|
266
|
+
ActiveOrganizationStorage.set(org.id);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
ActiveOrganizationStorage.clear();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const authError = err instanceof Error ? err : new Error(String(err));
|
|
274
|
+
setError(authError);
|
|
275
|
+
throw authError;
|
|
276
|
+
}
|
|
277
|
+
}, [client]);
|
|
278
|
+
const createOrganization = useCallback(async (data) => {
|
|
279
|
+
setError(null);
|
|
280
|
+
try {
|
|
281
|
+
const org = await client.createOrganization(data);
|
|
282
|
+
// Refresh the list and set as active
|
|
283
|
+
await refreshOrganizations();
|
|
284
|
+
await switchOrganization(org.id);
|
|
285
|
+
return org;
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
const authError = err instanceof Error ? err : new Error(String(err));
|
|
289
|
+
setError(authError);
|
|
290
|
+
throw authError;
|
|
291
|
+
}
|
|
292
|
+
}, [client, refreshOrganizations, switchOrganization]);
|
|
293
|
+
const updateOrganization = useCallback(async (orgId, data) => {
|
|
294
|
+
const updated = await client.updateOrganization(orgId, data);
|
|
295
|
+
// Refresh local list & active org reference if it matches
|
|
296
|
+
await refreshOrganizations();
|
|
297
|
+
if (activeOrganization?.id === orgId) {
|
|
298
|
+
setActiveOrganization(updated);
|
|
299
|
+
}
|
|
300
|
+
return updated;
|
|
301
|
+
}, [client, refreshOrganizations, activeOrganization]);
|
|
302
|
+
const deleteOrganization = useCallback(async (orgId) => {
|
|
303
|
+
await client.deleteOrganization(orgId);
|
|
304
|
+
if (activeOrganization?.id === orgId) {
|
|
305
|
+
setActiveOrganization(null);
|
|
306
|
+
ActiveOrganizationStorage.clear();
|
|
307
|
+
}
|
|
308
|
+
await refreshOrganizations();
|
|
309
|
+
}, [client, activeOrganization, refreshOrganizations]);
|
|
310
|
+
const leaveOrganization = useCallback(async (orgId) => {
|
|
311
|
+
await client.leaveOrganization(orgId);
|
|
312
|
+
if (activeOrganization?.id === orgId) {
|
|
313
|
+
setActiveOrganization(null);
|
|
314
|
+
ActiveOrganizationStorage.clear();
|
|
315
|
+
}
|
|
316
|
+
await refreshOrganizations();
|
|
317
|
+
}, [client, activeOrganization, refreshOrganizations]);
|
|
318
|
+
const getMembers = useCallback((orgId) => client.getMembers(orgId), [client]);
|
|
319
|
+
const inviteMember = useCallback((data) => client.inviteMember(data), [client]);
|
|
320
|
+
const removeMember = useCallback((data) => client.removeMember(data), [client]);
|
|
321
|
+
const updateMemberRole = useCallback((data) => client.updateMemberRole(data), [client]);
|
|
322
|
+
const listInvitations = useCallback((orgId) => client.listInvitations(orgId), [client]);
|
|
323
|
+
const cancelInvitation = useCallback((invitationId) => client.cancelInvitation(invitationId), [client]);
|
|
324
|
+
const getInvitation = useCallback((invitationId) => client.getInvitation(invitationId), [client]);
|
|
325
|
+
const acceptInvitation = useCallback(async (invitationId) => {
|
|
326
|
+
await client.acceptInvitation(invitationId);
|
|
327
|
+
await refreshOrganizations();
|
|
328
|
+
}, [client, refreshOrganizations]);
|
|
329
|
+
const rejectInvitation = useCallback((invitationId) => client.rejectInvitation(invitationId), [client]);
|
|
330
|
+
const listUserInvitations = useCallback(() => client.listUserInvitations(), [client]);
|
|
207
331
|
const value = useMemo(() => ({
|
|
208
332
|
user,
|
|
209
333
|
session,
|
|
@@ -218,6 +342,34 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
218
342
|
updateUser,
|
|
219
343
|
forgotPassword,
|
|
220
344
|
resetPassword,
|
|
221
|
-
|
|
345
|
+
getAuthConfig,
|
|
346
|
+
signInWithProvider,
|
|
347
|
+
organizations,
|
|
348
|
+
activeOrganization,
|
|
349
|
+
isOrganizationsLoading,
|
|
350
|
+
switchOrganization,
|
|
351
|
+
createOrganization,
|
|
352
|
+
refreshOrganizations,
|
|
353
|
+
updateOrganization,
|
|
354
|
+
deleteOrganization,
|
|
355
|
+
leaveOrganization,
|
|
356
|
+
getMembers,
|
|
357
|
+
inviteMember,
|
|
358
|
+
removeMember,
|
|
359
|
+
updateMemberRole,
|
|
360
|
+
listInvitations,
|
|
361
|
+
cancelInvitation,
|
|
362
|
+
getInvitation,
|
|
363
|
+
acceptInvitation,
|
|
364
|
+
rejectInvitation,
|
|
365
|
+
listUserInvitations,
|
|
366
|
+
}), [
|
|
367
|
+
user, session, isAuthenticated, isLoading, error, isPreviewMode, previewMode,
|
|
368
|
+
signIn, signUp, signOut, updateUser, forgotPassword, resetPassword, getAuthConfig, signInWithProvider,
|
|
369
|
+
organizations, activeOrganization, isOrganizationsLoading, switchOrganization, createOrganization, refreshOrganizations,
|
|
370
|
+
updateOrganization, deleteOrganization, leaveOrganization,
|
|
371
|
+
getMembers, inviteMember, removeMember, updateMemberRole,
|
|
372
|
+
listInvitations, cancelInvitation, getInvitation, acceptInvitation, rejectInvitation, listUserInvitations,
|
|
373
|
+
]);
|
|
222
374
|
return _jsx(AuthCtx.Provider, { value: value, children: children });
|
|
223
375
|
}
|
package/dist/LoginForm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,OAAO,EACP,WAAyB,EACzB,iBAAsC,EACtC,KAAiC,EACjC,WAAyD,EACzD,aAAa,EAAE,QAAsB,EACrC,MAAW,GACZ,EAAE,cAAc,2CA+GhB"}
|
package/dist/LoginForm.js
CHANGED
|
@@ -8,6 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
*/
|
|
9
9
|
import { useState } from 'react';
|
|
10
10
|
import { useAuth } from './useAuth';
|
|
11
|
+
import { SocialSignInButtons } from './SocialSignInButtons';
|
|
11
12
|
const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
|
|
12
13
|
/**
|
|
13
14
|
* Login form component with email/password authentication.
|
|
@@ -51,5 +52,5 @@ export function LoginForm({ onSuccess, onError, registerUrl = '/register', forgo
|
|
|
51
52
|
onError?.(authError);
|
|
52
53
|
}
|
|
53
54
|
};
|
|
54
|
-
return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px]", children: [_jsxs("div", { className: "flex flex-col space-y-2 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "login-email", className: "text-sm font-medium leading-none", children: l.emailLabel }), _jsx("input", { id: "login-email", type: "email", placeholder: l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "email", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: "login-password", className: "text-sm font-medium leading-none", children: l.passwordLabel }), forgotPasswordUrl && (_jsx(LinkComp, { href: forgotPasswordUrl, className: "text-sm text-primary underline-offset-4 hover:underline", children: l.forgotPasswordText }))] }), _jsx("input", { id: "login-password", type: "password", placeholder: l.passwordPlaceholder, value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsx("button", { type: "submit", disabled: isLoading, className: "inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: isLoading ? l.submittingButton : l.submitButton })] }), registerUrl && (_jsxs("p", { className: "px-8 text-center text-sm text-muted-foreground", children: [l.noAccountText, ' ', _jsx(LinkComp, { href: registerUrl, className: "text-primary underline-offset-4 hover:underline", children: l.signUpText })] }))] }));
|
|
55
|
+
return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px]", children: [_jsxs("div", { className: "flex flex-col space-y-2 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsx(SocialSignInButtons, { mode: "sign-in" }), error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "login-email", className: "text-sm font-medium leading-none", children: l.emailLabel }), _jsx("input", { id: "login-email", type: "email", placeholder: l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "email", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: "login-password", className: "text-sm font-medium leading-none", children: l.passwordLabel }), forgotPasswordUrl && (_jsx(LinkComp, { href: forgotPasswordUrl, className: "text-sm text-primary underline-offset-4 hover:underline", children: l.forgotPasswordText }))] }), _jsx("input", { id: "login-password", type: "password", placeholder: l.passwordPlaceholder, value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsx("button", { type: "submit", disabled: isLoading, className: "inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: isLoading ? l.submittingButton : l.submitButton })] }), registerUrl && (_jsxs("p", { className: "px-8 text-center text-sm text-muted-foreground", children: [l.noAccountText, ' ', _jsx(LinkComp, { href: registerUrl, className: "text-primary underline-offset-4 hover:underline", children: l.signUpText })] }))] }));
|
|
55
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegisterForm.d.ts","sourceRoot":"","sources":["../src/RegisterForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"RegisterForm.d.ts","sourceRoot":"","sources":["../src/RegisterForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,OAAO,EACP,QAAmB,EACnB,KAA2B,EAC3B,WAAqD,EACrD,aAAa,EAAE,QAAsB,EACrC,MAAW,GACZ,EAAE,iBAAiB,2CA0JnB"}
|
package/dist/RegisterForm.js
CHANGED
|
@@ -8,6 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
*/
|
|
9
9
|
import { useState } from 'react';
|
|
10
10
|
import { useAuth } from './useAuth';
|
|
11
|
+
import { SocialSignInButtons } from './SocialSignInButtons';
|
|
11
12
|
const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
|
|
12
13
|
/**
|
|
13
14
|
* Registration form component with name, email, and password fields.
|
|
@@ -65,5 +66,5 @@ export function RegisterForm({ onSuccess, onError, loginUrl = '/login', title =
|
|
|
65
66
|
onError?.(authError);
|
|
66
67
|
}
|
|
67
68
|
};
|
|
68
|
-
return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px]", children: [_jsxs("div", { className: "flex flex-col space-y-2 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-name", className: "text-sm font-medium leading-none", children: l.nameLabel }), _jsx("input", { id: "register-name", type: "text", placeholder: l.namePlaceholder, value: name, onChange: (e) => setName(e.target.value), required: true, autoComplete: "name", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-email", className: "text-sm font-medium leading-none", children: l.emailLabel }), _jsx("input", { id: "register-email", type: "email", placeholder: l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "email", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-password", className: "text-sm font-medium leading-none", children: l.passwordLabel }), _jsx("input", { id: "register-password", type: "password", placeholder: l.passwordPlaceholder, value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 8, autoComplete: "new-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-confirm-password", className: "text-sm font-medium leading-none", children: l.confirmPasswordLabel }), _jsx("input", { id: "register-confirm-password", type: "password", placeholder: l.confirmPasswordPlaceholder, value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 8, autoComplete: "new-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsx("button", { type: "submit", disabled: isLoading, className: "inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: isLoading ? l.submittingButton : l.submitButton })] }), loginUrl && (_jsxs("p", { className: "px-8 text-center text-sm text-muted-foreground", children: [l.hasAccountText, ' ', _jsx(LinkComp, { href: loginUrl, className: "text-primary underline-offset-4 hover:underline", children: l.signInText })] }))] }));
|
|
69
|
+
return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[380px]", children: [_jsxs("div", { className: "flex flex-col space-y-2 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsx(SocialSignInButtons, { mode: "sign-up" }), error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-name", className: "text-sm font-medium leading-none", children: l.nameLabel }), _jsx("input", { id: "register-name", type: "text", placeholder: l.namePlaceholder, value: name, onChange: (e) => setName(e.target.value), required: true, autoComplete: "name", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-email", className: "text-sm font-medium leading-none", children: l.emailLabel }), _jsx("input", { id: "register-email", type: "email", placeholder: l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "email", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-password", className: "text-sm font-medium leading-none", children: l.passwordLabel }), _jsx("input", { id: "register-password", type: "password", placeholder: l.passwordPlaceholder, value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 8, autoComplete: "new-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "register-confirm-password", className: "text-sm font-medium leading-none", children: l.confirmPasswordLabel }), _jsx("input", { id: "register-confirm-password", type: "password", placeholder: l.confirmPasswordPlaceholder, value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 8, autoComplete: "new-password", disabled: isLoading, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" })] }), _jsx("button", { type: "submit", disabled: isLoading, className: "inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: isLoading ? l.submittingButton : l.submitButton })] }), loginUrl && (_jsxs("p", { className: "px-8 text-center text-sm text-muted-foreground", children: [l.hasAccountText, ' ', _jsx(LinkComp, { href: loginUrl, className: "text-primary underline-offset-4 hover:underline", children: l.signInText })] }))] }));
|
|
69
70
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
export interface SocialSignInButtonsProps {
|
|
9
|
+
/** Sign-in vs sign-up changes the button label ("Continue with" vs "Sign up with") */
|
|
10
|
+
mode?: 'sign-in' | 'sign-up';
|
|
11
|
+
/** Where the provider should redirect after success. Defaults to current page. */
|
|
12
|
+
callbackURL?: string;
|
|
13
|
+
/** Where the provider should redirect on error. Defaults to current page. */
|
|
14
|
+
errorCallbackURL?: string;
|
|
15
|
+
/** Divider text shown between social buttons and the email form */
|
|
16
|
+
dividerText?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Renders one button per enabled third-party provider returned by
|
|
20
|
+
* `GET {authUrl}/config`. Clicking a button initiates an OAuth redirect via
|
|
21
|
+
* better-auth (`signIn.social` or `signIn.oauth2`).
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` while loading or when the server reports no providers.
|
|
24
|
+
*/
|
|
25
|
+
export declare function SocialSignInButtons({ mode, callbackURL, errorCallbackURL, dividerText, }: SocialSignInButtonsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
26
|
+
//# sourceMappingURL=SocialSignInButtons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocialSignInButtons.d.ts","sourceRoot":"","sources":["../src/SocialSignInButtons.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmFH,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,WAAsC,GACvC,EAAE,wBAAwB,kDA2E1B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectUI
|
|
4
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import { useEffect, useState } from 'react';
|
|
10
|
+
import { useAuth } from './useAuth';
|
|
11
|
+
// Brand name overrides for providers whose `name` from the server may be unset
|
|
12
|
+
// or where we want to canonicalize casing.
|
|
13
|
+
const PROVIDER_LABEL = {
|
|
14
|
+
google: 'Google',
|
|
15
|
+
github: 'GitHub',
|
|
16
|
+
microsoft: 'Microsoft',
|
|
17
|
+
apple: 'Apple',
|
|
18
|
+
facebook: 'Facebook',
|
|
19
|
+
twitter: 'Twitter',
|
|
20
|
+
discord: 'Discord',
|
|
21
|
+
gitlab: 'GitLab',
|
|
22
|
+
linkedin: 'LinkedIn',
|
|
23
|
+
};
|
|
24
|
+
// Inline brand-mark SVGs (single-color, currentColor where possible). Kept inline
|
|
25
|
+
// so the package has zero new icon-library dependencies.
|
|
26
|
+
const PROVIDER_ICON = {
|
|
27
|
+
google: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: _jsx("path", { fill: "#EA4335", d: "M12 10.2v3.9h5.5c-.24 1.4-1.7 4.1-5.5 4.1-3.3 0-6-2.74-6-6.1s2.7-6.1 6-6.1c1.88 0 3.14.8 3.86 1.5l2.64-2.55C16.84 3.36 14.66 2.4 12 2.4 6.92 2.4 2.8 6.52 2.8 11.6S6.92 20.8 12 20.8c6.92 0 9.2-4.86 9.2-7.4 0-.5-.06-.88-.14-1.2H12z" }) })),
|
|
28
|
+
github: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 fill-current", "aria-hidden": "true", children: _jsx("path", { d: "M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.56v-2.07c-3.2.7-3.87-1.37-3.87-1.37-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.75 2.68 1.25 3.34.96.1-.74.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.18 1.18a11.04 11.04 0 015.79 0c2.21-1.49 3.18-1.18 3.18-1.18.62 1.58.23 2.75.11 3.04.74.81 1.18 1.84 1.18 3.1 0 4.42-2.7 5.39-5.27 5.68.41.36.78 1.06.78 2.13v3.16c0 .31.21.68.8.56C20.21 21.39 23.5 17.08 23.5 12 23.5 5.65 18.35.5 12 .5z" }) })),
|
|
29
|
+
microsoft: (_jsxs("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: [_jsx("path", { fill: "#F25022", d: "M2 2h9.5v9.5H2z" }), _jsx("path", { fill: "#7FBA00", d: "M12.5 2H22v9.5h-9.5z" }), _jsx("path", { fill: "#00A4EF", d: "M2 12.5h9.5V22H2z" }), _jsx("path", { fill: "#FFB900", d: "M12.5 12.5H22V22h-9.5z" })] })),
|
|
30
|
+
apple: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 fill-current", "aria-hidden": "true", children: _jsx("path", { d: "M16.37 12.55c-.02-2.27 1.85-3.36 1.94-3.42-1.06-1.55-2.71-1.76-3.3-1.79-1.4-.14-2.74.83-3.45.83-.71 0-1.81-.81-2.98-.79-1.53.02-2.95.89-3.74 2.27-1.6 2.77-.41 6.86 1.14 9.11.76 1.1 1.66 2.34 2.83 2.3 1.14-.05 1.57-.74 2.94-.74 1.37 0 1.76.74 2.96.71 1.22-.02 2-1.12 2.75-2.23.87-1.28 1.22-2.52 1.24-2.59-.03-.01-2.38-.91-2.4-3.66zM14.1 5.31c.62-.76 1.05-1.81.93-2.86-.9.04-2 .6-2.65 1.35-.58.66-1.1 1.74-.96 2.76 1.01.08 2.04-.51 2.68-1.25z" }) })),
|
|
31
|
+
facebook: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: _jsx("path", { fill: "#1877F2", d: "M24 12C24 5.37 18.63 0 12 0S0 5.37 0 12c0 5.99 4.39 10.95 10.13 11.85V15.47H7.08V12h3.05V9.36c0-3.01 1.79-4.67 4.53-4.67 1.31 0 2.69.23 2.69.23v2.96h-1.51c-1.49 0-1.96.93-1.96 1.88V12h3.33l-.53 3.47h-2.8v8.38C19.61 22.95 24 17.99 24 12z" }) })),
|
|
32
|
+
twitter: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 fill-current", "aria-hidden": "true", children: _jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) })),
|
|
33
|
+
discord: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: _jsx("path", { fill: "#5865F2", d: "M20.32 4.37A19.79 19.79 0 0016.56 3l-.18.36a18.27 18.27 0 00-8.76 0L7.44 3a19.79 19.79 0 00-3.76 1.37C.99 8.4.27 12.34.63 16.22a19.94 19.94 0 006.07 3.07l.49-.7a13.2 13.2 0 01-2.07-.99c.17-.13.34-.27.5-.41a14.13 14.13 0 0012.76 0c.16.14.33.28.5.41-.66.4-1.36.73-2.07.99l.49.7a19.92 19.92 0 006.07-3.07c.43-4.5-.66-8.41-2.95-11.85zM8.52 14.12c-1.18 0-2.15-1.08-2.15-2.41 0-1.33.95-2.42 2.15-2.42 1.2 0 2.17 1.09 2.15 2.42 0 1.33-.96 2.41-2.15 2.41zm6.96 0c-1.18 0-2.15-1.08-2.15-2.41 0-1.33.95-2.42 2.15-2.42 1.2 0 2.17 1.09 2.15 2.42 0 1.33-.95 2.41-2.15 2.41z" }) })),
|
|
34
|
+
gitlab: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: _jsx("path", { fill: "#FC6D26", d: "M23.6 9.6L23.57 9.5 20.3.81a.84.84 0 00-1.6.05L16.5 7.6H7.5L5.3.86a.84.84 0 00-1.6-.05L.43 9.5l-.03.1a5.84 5.84 0 001.94 6.74l.01.01.03.02 4.8 3.6 2.38 1.8 1.45 1.1a1 1 0 001.2 0l1.45-1.1 2.38-1.8 4.83-3.62.01-.01a5.84 5.84 0 001.93-6.74z" }) })),
|
|
35
|
+
linkedin: (_jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", "aria-hidden": "true", children: _jsx("path", { fill: "#0A66C2", d: "M20.45 20.45h-3.55v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.36V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 11.01-4.13 2.06 2.06 0 010 4.13zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z" }) })),
|
|
36
|
+
};
|
|
37
|
+
function ProviderIcon({ id }) {
|
|
38
|
+
const icon = PROVIDER_ICON[id];
|
|
39
|
+
if (icon)
|
|
40
|
+
return _jsx("span", { className: "mr-2 flex h-4 w-4 items-center justify-center", children: icon });
|
|
41
|
+
return (_jsx("span", { className: "mr-2 flex h-4 w-4 items-center justify-center rounded-sm bg-muted text-[10px] font-bold uppercase", children: id[0] }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Renders one button per enabled third-party provider returned by
|
|
45
|
+
* `GET {authUrl}/config`. Clicking a button initiates an OAuth redirect via
|
|
46
|
+
* better-auth (`signIn.social` or `signIn.oauth2`).
|
|
47
|
+
*
|
|
48
|
+
* Returns `null` while loading or when the server reports no providers.
|
|
49
|
+
*/
|
|
50
|
+
export function SocialSignInButtons({ mode = 'sign-in', callbackURL, errorCallbackURL, dividerText = 'or continue with email', }) {
|
|
51
|
+
const { getAuthConfig, signInWithProvider } = useAuth();
|
|
52
|
+
const [providers, setProviders] = useState([]);
|
|
53
|
+
const [loading, setLoading] = useState(true);
|
|
54
|
+
const [error, setError] = useState(null);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
let cancelled = false;
|
|
57
|
+
// Wrap in Promise.resolve so synchronous throws (e.g. mock client without
|
|
58
|
+
// getConfig) become rejections we can swallow rather than uncaught errors.
|
|
59
|
+
Promise.resolve()
|
|
60
|
+
.then(() => getAuthConfig())
|
|
61
|
+
.then((config) => {
|
|
62
|
+
if (cancelled)
|
|
63
|
+
return;
|
|
64
|
+
const list = config?.socialProviders ?? [];
|
|
65
|
+
setProviders(list.filter((p) => p.enabled));
|
|
66
|
+
})
|
|
67
|
+
.catch((err) => {
|
|
68
|
+
if (cancelled)
|
|
69
|
+
return;
|
|
70
|
+
// Don't surface as a hard error — providers are an enhancement, not required.
|
|
71
|
+
console.warn('[SocialSignInButtons] failed to load auth config', err);
|
|
72
|
+
})
|
|
73
|
+
.finally(() => {
|
|
74
|
+
if (!cancelled)
|
|
75
|
+
setLoading(false);
|
|
76
|
+
});
|
|
77
|
+
return () => { cancelled = true; };
|
|
78
|
+
}, [getAuthConfig]);
|
|
79
|
+
if (loading || providers.length === 0)
|
|
80
|
+
return null;
|
|
81
|
+
const label = mode === 'sign-in' ? 'Continue with' : 'Sign up with';
|
|
82
|
+
const defaultCallback = typeof window !== 'undefined' ? window.location.href : undefined;
|
|
83
|
+
const onClick = async (provider) => {
|
|
84
|
+
setError(null);
|
|
85
|
+
try {
|
|
86
|
+
await signInWithProvider(provider.id, {
|
|
87
|
+
callbackURL: callbackURL ?? defaultCallback,
|
|
88
|
+
errorCallbackURL: errorCallbackURL ?? callbackURL ?? defaultCallback,
|
|
89
|
+
type: provider.type ?? 'social',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), providers.map((p) => (_jsxs("button", { type: "button", onClick: () => onClick(p), className: "inline-flex h-10 w-full items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: [_jsx(ProviderIcon, { id: p.id }), label, " ", PROVIDER_LABEL[p.id] ?? p.name] }, p.id))), _jsxs("div", { className: "relative my-1", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("span", { className: "w-full border-t" }) }), _jsx("div", { className: "relative flex justify-center text-xs uppercase", children: _jsx("span", { className: "bg-background px-2 text-muted-foreground", children: dividerText }) })] })] }));
|
|
97
|
+
}
|