@drmhse/authos-react 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -146,10 +146,14 @@ Registration form for new users.
146
146
  <SignUp
147
147
  onSuccess={() => console.log('Check your email!')}
148
148
  onError={(error) => console.error(error)}
149
- orgSlug="my-org" // Optional: pre-fill organization
149
+ orgSlug="my-org" // Optional: override provider config
150
+ serviceSlug="my-app" // Optional: override provider config
150
151
  />
151
152
  ```
152
153
 
154
+ > [!NOTE]
155
+ > The `orgSlug` and `serviceSlug` props are optional and will fallback to the values configured in `AuthOSProvider`. Only use these props if you need to override the provider configuration for a specific use case.
156
+
153
157
  ### MagicLinkSignIn
154
158
 
155
159
  Sign-in component for Magic Links (passwordless).
@@ -407,6 +411,8 @@ If you prefer to completely style the components yourself, you can target the da
407
411
  [data-authos-oauth] { /* OAuth button */ }
408
412
  [data-authos-oauth][data-provider="github"] { /* GitHub button */ }
409
413
  [data-authos-divider] { /* "or" divider */ }
414
+ [data-authos-magic-link] { /* Magic Link container */ }
415
+ [data-authos-passkey] { /* Passkey container */ }
410
416
  ```
411
417
 
412
418
  ## Security Considerations
package/dist/index.d.mts CHANGED
@@ -199,6 +199,19 @@ interface SignUpProps {
199
199
  showSignIn?: boolean;
200
200
  /** Custom class name for the form container */
201
201
  className?: string;
202
+ /**
203
+ * OAuth providers to display as buttons.
204
+ * Set to false to hide all OAuth buttons, or provide an array of providers.
205
+ * @default false (no OAuth buttons shown by default)
206
+ * @example ['github', 'google', 'microsoft']
207
+ */
208
+ providers?: ('github' | 'google' | 'microsoft')[] | false;
209
+ /**
210
+ * Show a divider between OAuth and email/password forms.
211
+ * Only visible when providers are enabled.
212
+ * @default true
213
+ */
214
+ showDivider?: boolean;
202
215
  }
203
216
  /**
204
217
  * Props for the OrganizationSwitcher component
@@ -501,12 +514,13 @@ declare function SignIn({ onSuccess, onError, showForgotPassword, showSignUp, cl
501
514
  * <SignUp
502
515
  * onSuccess={() => console.log('Registration successful!')}
503
516
  * onError={(error) => console.error('Registration failed:', error)}
517
+ * providers={['github', 'google']}
504
518
  * />
505
519
  * );
506
520
  * }
507
521
  * ```
508
522
  */
509
- declare function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn, className }: SignUpProps): react_jsx_runtime.JSX.Element;
523
+ declare function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn, className, providers, showDivider, }: SignUpProps): react_jsx_runtime.JSX.Element;
510
524
 
511
525
  /**
512
526
  * Component for switching between organizations.
package/dist/index.d.ts CHANGED
@@ -199,6 +199,19 @@ interface SignUpProps {
199
199
  showSignIn?: boolean;
200
200
  /** Custom class name for the form container */
201
201
  className?: string;
202
+ /**
203
+ * OAuth providers to display as buttons.
204
+ * Set to false to hide all OAuth buttons, or provide an array of providers.
205
+ * @default false (no OAuth buttons shown by default)
206
+ * @example ['github', 'google', 'microsoft']
207
+ */
208
+ providers?: ('github' | 'google' | 'microsoft')[] | false;
209
+ /**
210
+ * Show a divider between OAuth and email/password forms.
211
+ * Only visible when providers are enabled.
212
+ * @default true
213
+ */
214
+ showDivider?: boolean;
202
215
  }
203
216
  /**
204
217
  * Props for the OrganizationSwitcher component
@@ -501,12 +514,13 @@ declare function SignIn({ onSuccess, onError, showForgotPassword, showSignUp, cl
501
514
  * <SignUp
502
515
  * onSuccess={() => console.log('Registration successful!')}
503
516
  * onError={(error) => console.error('Registration failed:', error)}
517
+ * providers={['github', 'google']}
504
518
  * />
505
519
  * );
506
520
  * }
507
521
  * ```
508
522
  */
509
- declare function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn, className }: SignUpProps): react_jsx_runtime.JSX.Element;
523
+ declare function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn, className, providers, showDivider, }: SignUpProps): react_jsx_runtime.JSX.Element;
510
524
 
511
525
  /**
512
526
  * Component for switching between organizations.
package/dist/index.js CHANGED
@@ -87,26 +87,37 @@ var AUTHOS_STYLES = `
87
87
  }
88
88
 
89
89
  /* ==========================================================================
90
- Base Form Styles
90
+ Base Form Styles (Card Layout)
91
91
  ========================================================================== */
92
92
 
93
93
  [data-authos-signin],
94
94
  [data-authos-signup],
95
- [data-authos-magiclink],
95
+ [data-authos-magic-link],
96
96
  [data-authos-passkey] {
97
97
  font-family: var(--authos-font-family);
98
98
  font-size: var(--authos-font-size-sm);
99
99
  color: var(--authos-color-foreground);
100
+
101
+ /* Card Styling */
102
+ background-color: var(--authos-color-surface);
103
+ border: 1px solid var(--authos-color-border);
104
+ border-radius: var(--authos-border-radius-lg);
105
+ box-shadow: var(--authos-shadow);
106
+ padding: 2rem;
100
107
  width: 100%;
108
+ max-width: 25rem; /* 400px */
109
+ margin: 0 auto; /* Center horizontally */
101
110
  }
102
111
 
112
+ /* Ensure forms inside take full width */
103
113
  [data-authos-signin] form,
104
114
  [data-authos-signup] form,
105
- [data-authos-magiclink] form,
115
+ [data-authos-magic-link] form,
106
116
  [data-authos-passkey] form {
107
117
  display: flex;
108
118
  flex-direction: column;
109
119
  gap: var(--authos-spacing-md);
120
+ width: 100%;
110
121
  }
111
122
 
112
123
  /* ==========================================================================
@@ -430,39 +441,95 @@ var AUTHOS_STYLES = `
430
441
  ========================================================================== */
431
442
 
432
443
  [data-authos-orgswitcher] {
444
+ position: relative;
433
445
  font-family: var(--authos-font-family);
434
446
  font-size: var(--authos-font-size-sm);
447
+ color: var(--authos-color-foreground);
435
448
  }
436
449
 
437
- [data-authos-orgswitcher] select {
438
- padding: 0.5rem 2rem 0.5rem 0.75rem;
450
+ [data-authos-org-trigger] {
451
+ display: flex;
452
+ align-items: center;
453
+ justify-content: space-between;
454
+ gap: var(--authos-spacing-sm);
455
+ width: 100%;
456
+ padding: 0.5rem 0.75rem;
439
457
  font-size: var(--authos-font-size-sm);
440
458
  font-family: inherit;
441
459
  color: var(--authos-color-foreground);
442
- background-color: var(--authos-color-input);
443
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
444
- background-repeat: no-repeat;
445
- background-position: right 0.5rem center;
460
+ background-color: var(--authos-color-surface);
446
461
  border: 1px solid var(--authos-color-input-border);
447
462
  border-radius: var(--authos-border-radius);
448
463
  cursor: pointer;
449
464
  outline: none;
450
- appearance: none;
451
- -webkit-appearance: none;
452
- -moz-appearance: none;
453
465
  transition: border-color var(--authos-transition), box-shadow var(--authos-transition);
454
466
  }
455
467
 
456
- [data-authos-orgswitcher] select:focus {
468
+ [data-authos-org-trigger]:focus-visible {
457
469
  border-color: var(--authos-color-input-focus);
458
470
  box-shadow: 0 0 0 3px var(--authos-color-ring);
459
471
  }
460
472
 
461
- [data-authos-orgswitcher] select:disabled {
473
+ [data-authos-org-trigger][disabled] {
462
474
  opacity: 0.6;
463
475
  cursor: not-allowed;
464
476
  }
465
477
 
478
+ [data-authos-org-chevron] {
479
+ font-size: 0.625rem;
480
+ color: var(--authos-color-muted);
481
+ }
482
+
483
+ [data-authos-org-list] {
484
+ position: absolute;
485
+ top: 100%;
486
+ left: 0;
487
+ right: 0;
488
+ margin-top: var(--authos-spacing-xs);
489
+ padding: var(--authos-spacing-xs);
490
+ background-color: var(--authos-color-surface);
491
+ border: 1px solid var(--authos-color-border);
492
+ border-radius: var(--authos-border-radius);
493
+ box-shadow: var(--authos-shadow-lg);
494
+ list-style: none;
495
+ z-index: 50;
496
+ max-height: 240px;
497
+ overflow-y: auto;
498
+ animation: authos-fade-in 150ms ease-out;
499
+ }
500
+
501
+ [data-authos-org-item] {
502
+ display: flex;
503
+ align-items: center;
504
+ justify-content: space-between;
505
+ width: 100%;
506
+ padding: 0.5rem 0.75rem;
507
+ font-size: var(--authos-font-size-sm);
508
+ color: var(--authos-color-foreground);
509
+ background: transparent;
510
+ border: none;
511
+ border-radius: var(--authos-border-radius-sm);
512
+ cursor: pointer;
513
+ text-align: left;
514
+ transition: background-color var(--authos-transition);
515
+ }
516
+
517
+ [data-authos-org-item]:hover,
518
+ [data-authos-org-item]:focus-visible {
519
+ background-color: var(--authos-color-background);
520
+ outline: none;
521
+ }
522
+
523
+ [data-authos-org-item][data-active="true"] {
524
+ background-color: rgba(99, 102, 241, 0.05);
525
+ color: var(--authos-color-primary);
526
+ font-weight: 500;
527
+ }
528
+
529
+ [data-authos-org-item-check] {
530
+ color: var(--authos-color-primary);
531
+ }
532
+
466
533
  /* ==========================================================================
467
534
  Loading State
468
535
  ========================================================================== */
@@ -501,6 +568,16 @@ function AuthOSProvider({ config, children, client: externalClient, initialSessi
501
568
  if (config.appearance?.variables) {
502
569
  applyVariables(config.appearance.variables);
503
570
  }
571
+ if (config.org && !config.service) {
572
+ console.warn(
573
+ '[AuthOS] You provided "org" but not "service". OAuth flows may not work correctly.'
574
+ );
575
+ }
576
+ if (!config.org && config.service) {
577
+ console.warn(
578
+ '[AuthOS] You provided "service" but not "org". OAuth flows may not work correctly.'
579
+ );
580
+ }
504
581
  }, [config.appearance]);
505
582
  const hasInitialToken = react.useRef(!!initialSessionToken);
506
583
  react.useEffect(() => {
@@ -843,14 +920,25 @@ function SignIn({
843
920
  ] })
844
921
  ] });
845
922
  }
846
- function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn = true, className }) {
847
- const { client } = useAuthOSContext();
923
+ function SignUp({
924
+ onSuccess,
925
+ onError,
926
+ orgSlug,
927
+ serviceSlug,
928
+ showSignIn = true,
929
+ className,
930
+ providers = false,
931
+ showDivider = true
932
+ }) {
933
+ const { client, config } = useAuthOSContext();
848
934
  const [email, setEmail] = react.useState("");
849
935
  const [password, setPassword] = react.useState("");
850
936
  const [confirmPassword, setConfirmPassword] = react.useState("");
851
937
  const [error, setError] = react.useState(null);
852
938
  const [isLoading, setIsLoading] = react.useState(false);
853
939
  const [isSuccess, setIsSuccess] = react.useState(false);
940
+ const hasOAuthConfig = !!(config.org && config.service);
941
+ const oauthProviders = providers && Array.isArray(providers) ? providers : [];
854
942
  const handleSubmit = react.useCallback(
855
943
  async (e) => {
856
944
  e.preventDefault();
@@ -868,8 +956,8 @@ function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn = true, c
868
956
  await client.auth.register({
869
957
  email,
870
958
  password,
871
- org_slug: orgSlug,
872
- service_slug: serviceSlug
959
+ org_slug: orgSlug ?? config.org,
960
+ service_slug: serviceSlug ?? config.service
873
961
  });
874
962
  setIsSuccess(true);
875
963
  onSuccess?.();
@@ -893,63 +981,77 @@ function SignUp({ onSuccess, onError, orgSlug, serviceSlug, showSignIn = true, c
893
981
  ] })
894
982
  ] }) });
895
983
  }
896
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-authos-signup": true, "data-state": "form", children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
897
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "email", children: [
898
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-email", children: "Email" }),
899
- /* @__PURE__ */ jsxRuntime.jsx(
900
- "input",
901
- {
902
- id: "authos-signup-email",
903
- type: "email",
904
- autoComplete: "email",
905
- value: email,
906
- onChange: (e) => setEmail(e.target.value),
907
- placeholder: "Enter your email",
908
- required: true,
909
- disabled: isLoading
910
- }
911
- )
912
- ] }),
913
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "password", children: [
914
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-password", children: "Password" }),
915
- /* @__PURE__ */ jsxRuntime.jsx(
916
- "input",
917
- {
918
- id: "authos-signup-password",
919
- type: "password",
920
- autoComplete: "new-password",
921
- value: password,
922
- onChange: (e) => setPassword(e.target.value),
923
- placeholder: "Create a password",
924
- required: true,
925
- minLength: 8,
926
- disabled: isLoading
927
- }
928
- )
929
- ] }),
930
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "confirm-password", children: [
931
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-confirm", children: "Confirm Password" }),
932
- /* @__PURE__ */ jsxRuntime.jsx(
933
- "input",
984
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-authos-signup": true, "data-state": "form", children: [
985
+ oauthProviders.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-oauth-section": "", children: [
986
+ oauthProviders.map((provider) => /* @__PURE__ */ jsxRuntime.jsx(
987
+ OAuthButton,
934
988
  {
935
- id: "authos-signup-confirm",
936
- type: "password",
937
- autoComplete: "new-password",
938
- value: confirmPassword,
939
- onChange: (e) => setConfirmPassword(e.target.value),
940
- placeholder: "Confirm your password",
941
- required: true,
942
- disabled: isLoading
943
- }
944
- )
989
+ provider,
990
+ disabled: isLoading || !hasOAuthConfig
991
+ },
992
+ provider
993
+ )),
994
+ !hasOAuthConfig && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-authos-oauth-warning": "", style: { color: "orange", fontSize: "0.875rem" }, children: "OAuth requires org and service in AuthOSProvider config" })
945
995
  ] }),
946
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-authos-error": true, children: error }),
947
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": true, children: isLoading ? "Creating account..." : "Create Account" }),
948
- showSignIn && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-signin-prompt": true, children: [
949
- "Already have an account? ",
950
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/signin", "data-authos-link": "signin", children: "Sign in" })
996
+ oauthProviders.length > 0 && showDivider && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-authos-divider": "", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: "or" }) }),
997
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
998
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "email", children: [
999
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-email", children: "Email" }),
1000
+ /* @__PURE__ */ jsxRuntime.jsx(
1001
+ "input",
1002
+ {
1003
+ id: "authos-signup-email",
1004
+ type: "email",
1005
+ autoComplete: "email",
1006
+ value: email,
1007
+ onChange: (e) => setEmail(e.target.value),
1008
+ placeholder: "Enter your email",
1009
+ required: true,
1010
+ disabled: isLoading
1011
+ }
1012
+ )
1013
+ ] }),
1014
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "password", children: [
1015
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-password", children: "Password" }),
1016
+ /* @__PURE__ */ jsxRuntime.jsx(
1017
+ "input",
1018
+ {
1019
+ id: "authos-signup-password",
1020
+ type: "password",
1021
+ autoComplete: "new-password",
1022
+ value: password,
1023
+ onChange: (e) => setPassword(e.target.value),
1024
+ placeholder: "Create a password",
1025
+ required: true,
1026
+ minLength: 8,
1027
+ disabled: isLoading
1028
+ }
1029
+ )
1030
+ ] }),
1031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-field": "confirm-password", children: [
1032
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "authos-signup-confirm", children: "Confirm Password" }),
1033
+ /* @__PURE__ */ jsxRuntime.jsx(
1034
+ "input",
1035
+ {
1036
+ id: "authos-signup-confirm",
1037
+ type: "password",
1038
+ autoComplete: "new-password",
1039
+ value: confirmPassword,
1040
+ onChange: (e) => setConfirmPassword(e.target.value),
1041
+ placeholder: "Confirm your password",
1042
+ required: true,
1043
+ disabled: isLoading
1044
+ }
1045
+ )
1046
+ ] }),
1047
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-authos-error": true, children: error }),
1048
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": true, children: isLoading ? "Creating account..." : "Create Account" }),
1049
+ showSignIn && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-authos-signin-prompt": true, children: [
1050
+ "Already have an account? ",
1051
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/signin", "data-authos-link": "signin", children: "Sign in" })
1052
+ ] })
951
1053
  ] })
952
- ] }) });
1054
+ ] });
953
1055
  }
954
1056
  function OrganizationSwitcher({ onSwitch, className, renderItem }) {
955
1057
  const { client, isAuthenticated } = useAuthOSContext();