@drmhse/authos-react 0.1.2 → 0.1.4

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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createContext, useRef, useEffect, useState, useCallback, useMemo, useContext } from 'react';
2
2
  import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
3
3
  export { AuthErrorCodes, SsoApiError, SsoClient } from '@drmhse/sso-sdk';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
6
6
  // src/context.tsx
7
7
  var AuthOSContext = createContext(null);
@@ -49,6 +49,7 @@ function AuthOSProvider({ config, children, client: externalClient, initialSessi
49
49
  const contextValue = useMemo(
50
50
  () => ({
51
51
  client,
52
+ config,
52
53
  user,
53
54
  isAuthenticated: !!user,
54
55
  isLoading,
@@ -57,7 +58,7 @@ function AuthOSProvider({ config, children, client: externalClient, initialSessi
57
58
  setOrganization,
58
59
  refreshUser
59
60
  }),
60
- [client, user, isLoading, organization, refreshUser]
61
+ [client, config, user, isLoading, organization, refreshUser]
61
62
  );
62
63
  return /* @__PURE__ */ jsx(AuthOSContext.Provider, { value: contextValue, children });
63
64
  }
@@ -84,8 +85,12 @@ function useOrganization() {
84
85
  const { client, organization, setOrganization, refreshUser } = useAuthOSContext();
85
86
  const switchOrganization = useCallback(
86
87
  async (slug) => {
87
- const orgResponse = await client.organizations.get(slug);
88
- setOrganization(orgResponse.organization);
88
+ const result = await client.organizations.select(slug);
89
+ await client.setSession({
90
+ access_token: result.access_token,
91
+ refresh_token: result.refresh_token
92
+ });
93
+ setOrganization(result.organization);
89
94
  await refreshUser();
90
95
  },
91
96
  [client, setOrganization, refreshUser]
@@ -113,15 +118,71 @@ function useAllPermissions(permissions) {
113
118
  return permissions.every((perm) => user.permissions.includes(perm));
114
119
  }, [user?.permissions, permissions]);
115
120
  }
121
+ var PROVIDER_NAMES = {
122
+ github: "GitHub",
123
+ google: "Google",
124
+ microsoft: "Microsoft"
125
+ };
126
+ function OAuthButton({
127
+ provider,
128
+ children,
129
+ className,
130
+ onRedirect,
131
+ disabled = false
132
+ }) {
133
+ const { client, config } = useAuthOSContext();
134
+ const handleClick = useCallback(() => {
135
+ if (!config.org || !config.service) {
136
+ console.error(
137
+ `[AuthOS] OAuth login requires "org" and "service" in AuthOSProvider config.
138
+ Current config: { org: ${config.org ? `"${config.org}"` : "undefined"}, service: ${config.service ? `"${config.service}"` : "undefined"} }
139
+
140
+ Example:
141
+ <AuthOSProvider config={{
142
+ baseURL: "${config.baseURL}",
143
+ org: "your-org-slug",
144
+ service: "your-service-slug",
145
+ }}>
146
+
147
+ See: https://docs.authos.dev/react/oauth-setup`
148
+ );
149
+ return;
150
+ }
151
+ const redirectUri = config.redirectUri ?? (typeof window !== "undefined" ? window.location.origin + "/callback" : void 0);
152
+ const url = client.auth.getLoginUrl(provider, {
153
+ org: config.org,
154
+ service: config.service,
155
+ redirect_uri: redirectUri
156
+ });
157
+ onRedirect?.();
158
+ window.location.href = url;
159
+ }, [client, config, provider, onRedirect]);
160
+ return /* @__PURE__ */ jsx(
161
+ "button",
162
+ {
163
+ type: "button",
164
+ onClick: handleClick,
165
+ className,
166
+ disabled,
167
+ "data-authos-oauth": "",
168
+ "data-provider": provider,
169
+ children: children ?? `Continue with ${PROVIDER_NAMES[provider]}`
170
+ }
171
+ );
172
+ }
116
173
  var MFA_PREAUTH_EXPIRY = 300;
117
174
  function SignIn({
118
175
  onSuccess,
119
176
  onError,
120
177
  showForgotPassword = true,
121
178
  showSignUp = true,
122
- className
179
+ className,
180
+ providers = false,
181
+ showDivider = true
123
182
  }) {
124
- const { client, setUser } = useAuthOSContext();
183
+ const { client, config, setUser } = useAuthOSContext();
184
+ const hasOAuthConfig = !!(config.org && config.service);
185
+ const oauthProviders = providers && Array.isArray(providers) ? providers : [];
125
186
  const [state, setState] = useState("credentials");
126
187
  const [email, setEmail] = useState("");
127
188
  const [password, setPassword] = useState("");
@@ -204,47 +265,61 @@ function SignIn({
204
265
  /* @__PURE__ */ jsx("button", { type: "button", onClick: handleBackToCredentials, "data-authos-back": "", children: "Back to login" })
205
266
  ] }) });
206
267
  }
207
- return /* @__PURE__ */ jsx("div", { className, "data-authos-signin": "", "data-state": "credentials", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleCredentialsSubmit, children: [
208
- /* @__PURE__ */ jsxs("div", { "data-authos-field": "email", children: [
209
- /* @__PURE__ */ jsx("label", { htmlFor: "authos-email", children: "Email" }),
210
- /* @__PURE__ */ jsx(
211
- "input",
212
- {
213
- id: "authos-email",
214
- type: "email",
215
- autoComplete: "email",
216
- value: email,
217
- onChange: (e) => setEmail(e.target.value),
218
- placeholder: "Enter your email",
219
- required: true,
220
- disabled: isLoading
221
- }
222
- )
223
- ] }),
224
- /* @__PURE__ */ jsxs("div", { "data-authos-field": "password", children: [
225
- /* @__PURE__ */ jsx("label", { htmlFor: "authos-password", children: "Password" }),
226
- /* @__PURE__ */ jsx(
227
- "input",
268
+ return /* @__PURE__ */ jsxs("div", { className, "data-authos-signin": "", "data-state": "credentials", children: [
269
+ oauthProviders.length > 0 && /* @__PURE__ */ jsxs("div", { "data-authos-oauth-section": "", children: [
270
+ oauthProviders.map((provider) => /* @__PURE__ */ jsx(
271
+ OAuthButton,
228
272
  {
229
- id: "authos-password",
230
- type: "password",
231
- autoComplete: "current-password",
232
- value: password,
233
- onChange: (e) => setPassword(e.target.value),
234
- placeholder: "Enter your password",
235
- required: true,
236
- disabled: isLoading
237
- }
238
- )
273
+ provider,
274
+ disabled: isLoading || !hasOAuthConfig
275
+ },
276
+ provider
277
+ )),
278
+ !hasOAuthConfig && /* @__PURE__ */ jsx("p", { "data-authos-oauth-warning": "", style: { color: "orange", fontSize: "0.875rem" }, children: "OAuth requires org and service in AuthOSProvider config" })
239
279
  ] }),
240
- error && /* @__PURE__ */ jsx("div", { "data-authos-error": true, children: error }),
241
- /* @__PURE__ */ jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": "", children: isLoading ? "Signing in..." : "Sign In" }),
242
- showForgotPassword && /* @__PURE__ */ jsx("a", { href: "/forgot-password", "data-authos-link": "forgot-password", children: "Forgot password?" }),
243
- showSignUp && /* @__PURE__ */ jsxs("div", { "data-authos-signup-prompt": true, children: [
244
- "Don't have an account? ",
245
- /* @__PURE__ */ jsx("a", { href: "/signup", "data-authos-link": "signup", children: "Sign up" })
280
+ oauthProviders.length > 0 && showDivider && /* @__PURE__ */ jsx("div", { "data-authos-divider": "", children: /* @__PURE__ */ jsx("span", { children: "or" }) }),
281
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleCredentialsSubmit, children: [
282
+ /* @__PURE__ */ jsxs("div", { "data-authos-field": "email", children: [
283
+ /* @__PURE__ */ jsx("label", { htmlFor: "authos-email", children: "Email" }),
284
+ /* @__PURE__ */ jsx(
285
+ "input",
286
+ {
287
+ id: "authos-email",
288
+ type: "email",
289
+ autoComplete: "email",
290
+ value: email,
291
+ onChange: (e) => setEmail(e.target.value),
292
+ placeholder: "Enter your email",
293
+ required: true,
294
+ disabled: isLoading
295
+ }
296
+ )
297
+ ] }),
298
+ /* @__PURE__ */ jsxs("div", { "data-authos-field": "password", children: [
299
+ /* @__PURE__ */ jsx("label", { htmlFor: "authos-password", children: "Password" }),
300
+ /* @__PURE__ */ jsx(
301
+ "input",
302
+ {
303
+ id: "authos-password",
304
+ type: "password",
305
+ autoComplete: "current-password",
306
+ value: password,
307
+ onChange: (e) => setPassword(e.target.value),
308
+ placeholder: "Enter your password",
309
+ required: true,
310
+ disabled: isLoading
311
+ }
312
+ )
313
+ ] }),
314
+ error && /* @__PURE__ */ jsx("div", { "data-authos-error": true, children: error }),
315
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": "", children: isLoading ? "Signing in..." : "Sign In" }),
316
+ showForgotPassword && /* @__PURE__ */ jsx("a", { href: "/forgot-password", "data-authos-link": "forgot-password", children: "Forgot password?" }),
317
+ showSignUp && /* @__PURE__ */ jsxs("div", { "data-authos-signup-prompt": true, children: [
318
+ "Don't have an account? ",
319
+ /* @__PURE__ */ jsx("a", { href: "/signup", "data-authos-link": "signup", children: "Sign up" })
320
+ ] })
246
321
  ] })
247
- ] }) });
322
+ ] });
248
323
  }
249
324
  function SignUp({ onSuccess, onError, orgSlug, showSignIn = true, className }) {
250
325
  const { client } = useAuthOSContext();
@@ -533,5 +608,159 @@ function Protect({ permission, role, fallback = null, children }) {
533
608
  };
534
609
  return /* @__PURE__ */ jsx("div", { "data-authos-protect": true, children: renderContent() });
535
610
  }
611
+ function SignedIn({ children }) {
612
+ const { isAuthenticated, isLoading } = useAuthOSContext();
613
+ if (isLoading) {
614
+ return null;
615
+ }
616
+ if (!isAuthenticated) {
617
+ return null;
618
+ }
619
+ return /* @__PURE__ */ jsx(Fragment, { children });
620
+ }
621
+ function SignedOut({ children }) {
622
+ const { isAuthenticated, isLoading } = useAuthOSContext();
623
+ if (isLoading) {
624
+ return null;
625
+ }
626
+ if (isAuthenticated) {
627
+ return null;
628
+ }
629
+ return /* @__PURE__ */ jsx(Fragment, { children });
630
+ }
631
+ function MagicLinkSignIn({
632
+ onSuccess,
633
+ onError,
634
+ className,
635
+ showPasswordSignIn = true
636
+ }) {
637
+ const { client } = useAuthOSContext();
638
+ const [email, setEmail] = useState("");
639
+ const [isLoading, setIsLoading] = useState(false);
640
+ const [error, setError] = useState(null);
641
+ const [isSent, setIsSent] = useState(false);
642
+ const handleSubmit = useCallback(
643
+ async (e) => {
644
+ e.preventDefault();
645
+ setError(null);
646
+ setIsLoading(true);
647
+ try {
648
+ await client.magicLinks.request({ email });
649
+ setIsSent(true);
650
+ onSuccess?.();
651
+ } catch (err) {
652
+ const message = err instanceof SsoApiError ? err.message : "Failed to send magic link";
653
+ setError(message);
654
+ onError?.(err instanceof Error ? err : new Error(message));
655
+ } finally {
656
+ setIsLoading(false);
657
+ }
658
+ },
659
+ [client, email, onSuccess, onError]
660
+ );
661
+ if (isSent) {
662
+ return /* @__PURE__ */ jsxs("div", { className, "data-authos-magic-link": "", "data-state": "sent", children: [
663
+ /* @__PURE__ */ jsxs("div", { "data-authos-success": "", children: [
664
+ /* @__PURE__ */ jsx("p", { children: "Check your email!" }),
665
+ /* @__PURE__ */ jsxs("p", { children: [
666
+ "We sent a login link to ",
667
+ /* @__PURE__ */ jsx("strong", { children: email })
668
+ ] })
669
+ ] }),
670
+ /* @__PURE__ */ jsx(
671
+ "button",
672
+ {
673
+ type: "button",
674
+ onClick: () => setIsSent(false),
675
+ "data-authos-back": "",
676
+ children: "Use a different email"
677
+ }
678
+ )
679
+ ] });
680
+ }
681
+ return /* @__PURE__ */ jsx("div", { className, "data-authos-magic-link": "", "data-state": "form", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
682
+ /* @__PURE__ */ jsxs("div", { "data-authos-field": "email", children: [
683
+ /* @__PURE__ */ jsx("label", { htmlFor: "authos-magic-email", children: "Email" }),
684
+ /* @__PURE__ */ jsx(
685
+ "input",
686
+ {
687
+ id: "authos-magic-email",
688
+ type: "email",
689
+ autoComplete: "email",
690
+ value: email,
691
+ onChange: (e) => setEmail(e.target.value),
692
+ placeholder: "Enter your email",
693
+ required: true,
694
+ disabled: isLoading
695
+ }
696
+ )
697
+ ] }),
698
+ error && /* @__PURE__ */ jsx("div", { "data-authos-error": "", children: error }),
699
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": "", children: isLoading ? "Sending..." : "Send Magic Link" }),
700
+ showPasswordSignIn && /* @__PURE__ */ jsx("div", { "data-authos-signin-prompt": "", children: /* @__PURE__ */ jsx("a", { href: "/signin", "data-authos-link": "signin", children: "Sign in with password" }) })
701
+ ] }) });
702
+ }
703
+ function PasskeySignIn({
704
+ onSuccess,
705
+ onError,
706
+ className,
707
+ showPasswordSignIn = true
708
+ }) {
709
+ const { client, setUser } = useAuthOSContext();
710
+ const [email, setEmail] = useState("");
711
+ const [isLoading, setIsLoading] = useState(false);
712
+ const [error, setError] = useState(null);
713
+ const [isSupported, setIsSupported] = useState(true);
714
+ useEffect(() => {
715
+ setIsSupported(client.passkeys.isSupported());
716
+ }, [client]);
717
+ const handleSubmit = useCallback(
718
+ async (e) => {
719
+ e.preventDefault();
720
+ setError(null);
721
+ setIsLoading(true);
722
+ try {
723
+ await client.passkeys.login(email);
724
+ const profile = await client.user.getProfile();
725
+ setUser(profile);
726
+ onSuccess?.();
727
+ } catch (err) {
728
+ const message = err instanceof SsoApiError ? err.message : err instanceof Error ? err.message : "Passkey authentication failed";
729
+ setError(message);
730
+ onError?.(err instanceof Error ? err : new Error(message));
731
+ } finally {
732
+ setIsLoading(false);
733
+ }
734
+ },
735
+ [client, email, setUser, onSuccess, onError]
736
+ );
737
+ if (!isSupported) {
738
+ return /* @__PURE__ */ jsxs("div", { className, "data-authos-passkey": "", "data-state": "unsupported", children: [
739
+ /* @__PURE__ */ jsx("div", { "data-authos-error": "", children: "Passkeys are not supported in this browser." }),
740
+ showPasswordSignIn && /* @__PURE__ */ jsx("a", { href: "/signin", "data-authos-link": "signin", children: "Sign in with password" })
741
+ ] });
742
+ }
743
+ return /* @__PURE__ */ jsx("div", { className, "data-authos-passkey": "", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
744
+ /* @__PURE__ */ jsxs("div", { "data-authos-field": "email", children: [
745
+ /* @__PURE__ */ jsx("label", { htmlFor: "authos-passkey-email", children: "Email" }),
746
+ /* @__PURE__ */ jsx(
747
+ "input",
748
+ {
749
+ id: "authos-passkey-email",
750
+ type: "email",
751
+ autoComplete: "email webauthn",
752
+ value: email,
753
+ onChange: (e) => setEmail(e.target.value),
754
+ placeholder: "Enter your email",
755
+ required: true,
756
+ disabled: isLoading
757
+ }
758
+ )
759
+ ] }),
760
+ error && /* @__PURE__ */ jsx("div", { "data-authos-error": "", children: error }),
761
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isLoading, "data-authos-submit": "", children: isLoading ? "Authenticating..." : "Sign in with Passkey" }),
762
+ showPasswordSignIn && /* @__PURE__ */ jsx("div", { "data-authos-signin-prompt": "", children: /* @__PURE__ */ jsx("a", { href: "/signin", "data-authos-link": "signin", children: "Sign in with password" }) })
763
+ ] }) });
764
+ }
536
765
 
537
- export { AuthOSProvider, OrganizationSwitcher, Protect, SignIn, SignUp, UserButton, useAllPermissions, useAnyPermission, useAuthOS, useAuthOSContext, useOrganization, usePermission, useUser };
766
+ export { AuthOSProvider, MagicLinkSignIn, OAuthButton, OrganizationSwitcher, PasskeySignIn, Protect, SignIn, SignUp, SignedIn, SignedOut, UserButton, useAllPermissions, useAnyPermission, useAuthOS, useAuthOSContext, useOrganization, usePermission, useUser };
package/dist/nextjs.d.mts CHANGED
@@ -226,4 +226,33 @@ declare function createAuthOSClient(options: CreateAuthOSClientOptions): SsoClie
226
226
  */
227
227
  declare function createServerClient(token: string, baseURL: string): SsoClient;
228
228
 
229
- export { type AuthMiddlewareConfig, type AuthState, type AuthUser, type CreateAuthOSClientOptions, auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getToken };
229
+ /**
230
+ * Utility functions for Next.js integration
231
+ */
232
+ /**
233
+ * Construct the JWKS URL from a base URL.
234
+ *
235
+ * This helper simplifies middleware configuration by deriving the JWKS URL
236
+ * from the same base URL used in your client-side configuration.
237
+ *
238
+ * @param baseURL - The base URL of your AuthOS instance
239
+ * @returns The full JWKS URL
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * // middleware.ts
244
+ * import { getJwksUrl, authMiddleware } from '@drmhse/authos-react/nextjs';
245
+ *
246
+ * export default authMiddleware({
247
+ * jwksUrl: getJwksUrl(process.env.NEXT_PUBLIC_AUTHOS_URL!),
248
+ * protectedRoutes: ['/dashboard/*'],
249
+ * });
250
+ * ```
251
+ */
252
+ declare function getJwksUrl(baseURL: string): string;
253
+ /**
254
+ * Get the base URL without trailing slash
255
+ */
256
+ declare function normalizeBaseUrl(baseURL: string): string;
257
+
258
+ export { type AuthMiddlewareConfig, type AuthState, type AuthUser, type CreateAuthOSClientOptions, auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getJwksUrl, getToken, normalizeBaseUrl };
package/dist/nextjs.d.ts CHANGED
@@ -226,4 +226,33 @@ declare function createAuthOSClient(options: CreateAuthOSClientOptions): SsoClie
226
226
  */
227
227
  declare function createServerClient(token: string, baseURL: string): SsoClient;
228
228
 
229
- export { type AuthMiddlewareConfig, type AuthState, type AuthUser, type CreateAuthOSClientOptions, auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getToken };
229
+ /**
230
+ * Utility functions for Next.js integration
231
+ */
232
+ /**
233
+ * Construct the JWKS URL from a base URL.
234
+ *
235
+ * This helper simplifies middleware configuration by deriving the JWKS URL
236
+ * from the same base URL used in your client-side configuration.
237
+ *
238
+ * @param baseURL - The base URL of your AuthOS instance
239
+ * @returns The full JWKS URL
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * // middleware.ts
244
+ * import { getJwksUrl, authMiddleware } from '@drmhse/authos-react/nextjs';
245
+ *
246
+ * export default authMiddleware({
247
+ * jwksUrl: getJwksUrl(process.env.NEXT_PUBLIC_AUTHOS_URL!),
248
+ * protectedRoutes: ['/dashboard/*'],
249
+ * });
250
+ * ```
251
+ */
252
+ declare function getJwksUrl(baseURL: string): string;
253
+ /**
254
+ * Get the base URL without trailing slash
255
+ */
256
+ declare function normalizeBaseUrl(baseURL: string): string;
257
+
258
+ export { type AuthMiddlewareConfig, type AuthState, type AuthUser, type CreateAuthOSClientOptions, auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getJwksUrl, getToken, normalizeBaseUrl };
package/dist/nextjs.js CHANGED
@@ -5903,9 +5903,20 @@ function createServerClient(token, baseURL) {
5903
5903
  });
5904
5904
  }
5905
5905
 
5906
+ // src/nextjs/utils.ts
5907
+ function getJwksUrl(baseURL) {
5908
+ const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
5909
+ return `${base}/.well-known/jwks.json`;
5910
+ }
5911
+ function normalizeBaseUrl(baseURL) {
5912
+ return baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
5913
+ }
5914
+
5906
5915
  exports.auth = auth;
5907
5916
  exports.authMiddleware = authMiddleware;
5908
5917
  exports.createAuthOSClient = createAuthOSClient;
5909
5918
  exports.createServerClient = createServerClient;
5910
5919
  exports.currentUser = currentUser;
5920
+ exports.getJwksUrl = getJwksUrl;
5911
5921
  exports.getToken = getToken;
5922
+ exports.normalizeBaseUrl = normalizeBaseUrl;
package/dist/nextjs.mjs CHANGED
@@ -5901,4 +5901,13 @@ function createServerClient(token, baseURL) {
5901
5901
  });
5902
5902
  }
5903
5903
 
5904
- export { auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getToken };
5904
+ // src/nextjs/utils.ts
5905
+ function getJwksUrl(baseURL) {
5906
+ const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
5907
+ return `${base}/.well-known/jwks.json`;
5908
+ }
5909
+ function normalizeBaseUrl(baseURL) {
5910
+ return baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
5911
+ }
5912
+
5913
+ export { auth, authMiddleware, createAuthOSClient, createServerClient, currentUser, getJwksUrl, getToken, normalizeBaseUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drmhse/authos-react",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "React and Next.js adapter for AuthOS authentication",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -68,6 +68,6 @@
68
68
  "access": "public"
69
69
  },
70
70
  "dependencies": {
71
- "@drmhse/sso-sdk": "^0.3.4"
71
+ "@drmhse/sso-sdk": "^0.3.8"
72
72
  }
73
73
  }