autherr 2.0.2 → 2.0.31

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.
@@ -6,12 +6,13 @@ export async function createClientAssertion(clientId, privateKeyPem) {
6
6
  name: "RSASSA-PKCS1-v1_5",
7
7
  hash: "SHA-256",
8
8
  }, false, ["sign"]);
9
- const now = Math.floor(Date.now() / 1000);
10
- return await new SignJWT({ clientId })
9
+ return await new SignJWT({})
11
10
  .setProtectedHeader({ alg: "RS256", typ: "JWT" })
12
- .setIssuedAt(now)
13
- .setExpirationTime(now + 120) // ⏱ 2 minutes
14
- .setAudience("autherr")
15
- .setIssuer(clientId)
11
+ .setIssuer(clientId) // iss
12
+ .setSubject(clientId) // sub
13
+ .setAudience("autherr") // aud
14
+ .setIssuedAt() // iat = now
15
+ .setExpirationTime("2m") // exp = iat + 2 min
16
+ .setJti(crypto.randomUUID()) // anti-replay
16
17
  .sign(key);
17
18
  }
@@ -1,8 +1,8 @@
1
1
  export declare function useAutherr(): {
2
2
  accessToken: string | null;
3
3
  isAuthenticated: boolean;
4
- login: () => void;
5
- signup: () => void;
4
+ login: () => Promise<void>;
5
+ signup: () => Promise<void>;
6
6
  logout: () => Promise<void>;
7
7
  refreshSession: () => Promise<void>;
8
8
  getAccessToken: () => string | null;
@@ -2,8 +2,8 @@ import React from "react";
2
2
  interface AutherrContextValue {
3
3
  accessToken: string | null;
4
4
  isAuthenticated: boolean;
5
- login: () => void;
6
- signup: () => void;
5
+ login: () => Promise<void>;
6
+ signup: () => Promise<void>;
7
7
  logout: () => Promise<void>;
8
8
  getAccessToken: () => string | null;
9
9
  refreshSession: () => Promise<void>;
@@ -3,13 +3,6 @@ import { createContext, useContext, useEffect, useMemo, useState, } from "react"
3
3
  import { fetchSession } from "../api/session";
4
4
  import { logoutSession } from "../api/logout";
5
5
  import { createClientAssertion } from "../crypto/createClientAssertion";
6
- function setClientAssertionCookie(token) {
7
- document.cookie =
8
- `autherr_client_assertion=${token};` +
9
- `Path=/;` +
10
- `Secure;` +
11
- `SameSite=None`;
12
- }
13
6
  const AutherrContext = createContext(null);
14
7
  export function AutherrProvider({ children, clientId, baseUrl, clientPrivateKey, }) {
15
8
  const [accessToken, setAccessToken] = useState(null);
@@ -28,35 +21,31 @@ export function AutherrProvider({ children, clientId, baseUrl, clientPrivateKey,
28
21
  useEffect(() => {
29
22
  refreshSession();
30
23
  }, [baseUrl, clientId]);
31
- const login = async () => {
32
- const state = crypto.randomUUID();
33
- const assertion = await createClientAssertion(clientId, clientPrivateKey);
34
- setClientAssertionCookie(assertion);
35
- window.location.href =
36
- `${baseUrl}/auth/login` +
37
- `?client_id=${clientId}` +
38
- `&redirect_uri=${encodeURIComponent(window.location.origin)}` +
39
- `&state=${state}`;
40
- };
41
- const signup = async () => {
24
+ const buildRedirect = async (path) => {
42
25
  const state = crypto.randomUUID();
43
26
  const assertion = await createClientAssertion(clientId, clientPrivateKey);
44
- setClientAssertionCookie(assertion);
45
- window.location.href =
46
- `${baseUrl}/auth/signup` +
47
- `?client_id=${clientId}` +
48
- `&redirect_uri=${encodeURIComponent(window.location.origin)}` +
49
- `&state=${state}`;
27
+ const url = new URL(`${baseUrl}/auth/${path}`);
28
+ url.searchParams.set("client_id", clientId);
29
+ url.searchParams.set("redirect_uri", window.location.origin);
30
+ url.searchParams.set("state", state);
31
+ // Attach assertion as header via fetch redirect
32
+ await fetch(url.toString(), {
33
+ method: "GET",
34
+ headers: {
35
+ "X-Client-Assertion": assertion,
36
+ },
37
+ credentials: "include",
38
+ }).then((res) => {
39
+ window.location.href = res.url;
40
+ });
50
41
  };
42
+ const login = () => buildRedirect("login");
43
+ const signup = () => buildRedirect("signup");
51
44
  const logout = async () => {
52
- try {
53
- await logoutSession(baseUrl, clientId);
54
- }
55
- finally {
56
- setAccessToken(null);
57
- setIsAuthenticated(false);
58
- window.location.href = window.location.origin;
59
- }
45
+ await logoutSession(baseUrl, clientId);
46
+ setAccessToken(null);
47
+ setIsAuthenticated(false);
48
+ window.location.href = window.location.origin;
60
49
  };
61
50
  const value = useMemo(() => ({
62
51
  accessToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autherr",
3
- "version": "2.0.2",
3
+ "version": "2.0.31",
4
4
  "dest": "dist",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -17,13 +17,13 @@ export async function createClientAssertion(
17
17
  ["sign"]
18
18
  );
19
19
 
20
- const now = Math.floor(Date.now() / 1000);
21
-
22
- return await new SignJWT({ clientId })
20
+ return await new SignJWT({})
23
21
  .setProtectedHeader({ alg: "RS256", typ: "JWT" })
24
- .setIssuedAt(now)
25
- .setExpirationTime(now + 120) // ⏱ 2 minutes
26
- .setAudience("autherr")
27
- .setIssuer(clientId)
22
+ .setIssuer(clientId) // iss
23
+ .setSubject(clientId) // sub
24
+ .setAudience("autherr") // aud
25
+ .setIssuedAt() // iat = now
26
+ .setExpirationTime("2m") // exp = iat + 2 min
27
+ .setJti(crypto.randomUUID()) // anti-replay
28
28
  .sign(key);
29
29
  }
@@ -7,35 +7,24 @@ import React, {
7
7
  } from "react";
8
8
  import { fetchSession } from "../api/session";
9
9
  import { logoutSession } from "../api/logout";
10
- import { AutherrUser } from "../types/auth";
11
10
  import { createClientAssertion } from "../crypto/createClientAssertion";
12
11
 
13
12
  interface AutherrContextValue {
14
13
  accessToken: string | null;
15
14
  isAuthenticated: boolean;
16
- login: () => void;
17
- signup: () => void;
15
+ login: () => Promise<void>;
16
+ signup: () => Promise<void>;
18
17
  logout: () => Promise<void>;
19
18
  getAccessToken: () => string | null;
20
19
  refreshSession: () => Promise<void>;
21
20
  }
22
21
 
23
- function setClientAssertionCookie(token: string) {
24
- document.cookie =
25
- `autherr_client_assertion=${token};` +
26
- `Path=/;` +
27
- `Secure;` +
28
- `SameSite=None`;
29
- }
30
-
31
-
32
-
33
22
  const AutherrContext = createContext<AutherrContextValue | null>(null);
34
23
 
35
24
  interface AutherrProviderProps {
36
25
  children: React.ReactNode;
37
26
  clientId: string;
38
- baseUrl: string; // e.g. https://autherr.com
27
+ baseUrl: string;
39
28
  clientPrivateKey: string;
40
29
  }
41
30
 
@@ -45,13 +34,11 @@ export function AutherrProvider({
45
34
  baseUrl,
46
35
  clientPrivateKey,
47
36
  }: AutherrProviderProps) {
48
-
49
37
  const [accessToken, setAccessToken] = useState<string | null>(null);
50
38
  const [isAuthenticated, setIsAuthenticated] = useState(false);
51
39
 
52
40
  const refreshSession = async () => {
53
41
  const session = await fetchSession(baseUrl, clientId);
54
-
55
42
  if (session.authenticated && session.accessToken) {
56
43
  setAccessToken(session.accessToken);
57
44
  setIsAuthenticated(true);
@@ -61,12 +48,11 @@ export function AutherrProvider({
61
48
  }
62
49
  };
63
50
 
64
-
65
51
  useEffect(() => {
66
52
  refreshSession();
67
53
  }, [baseUrl, clientId]);
68
54
 
69
- const login = async () => {
55
+ const buildRedirect = async (path: "login" | "signup") => {
70
56
  const state = crypto.randomUUID();
71
57
 
72
58
  const assertion = await createClientAssertion(
@@ -74,41 +60,34 @@ export function AutherrProvider({
74
60
  clientPrivateKey
75
61
  );
76
62
 
77
- setClientAssertionCookie(assertion);
78
-
79
- window.location.href =
80
- `${baseUrl}/auth/login` +
81
- `?client_id=${clientId}` +
82
- `&redirect_uri=${encodeURIComponent(window.location.origin)}` +
83
- `&state=${state}`;
84
- };
85
-
86
- const signup = async () => {
87
- const state = crypto.randomUUID();
88
-
89
- const assertion = await createClientAssertion(
90
- clientId,
91
- clientPrivateKey
63
+ const url = new URL(`${baseUrl}/auth/${path}`);
64
+ url.searchParams.set("client_id", clientId);
65
+ url.searchParams.set(
66
+ "redirect_uri",
67
+ window.location.origin
92
68
  );
93
-
94
- setClientAssertionCookie(assertion);
95
-
96
- window.location.href =
97
- `${baseUrl}/auth/signup` +
98
- `?client_id=${clientId}` +
99
- `&redirect_uri=${encodeURIComponent(window.location.origin)}` +
100
- `&state=${state}`;
69
+ url.searchParams.set("state", state);
70
+
71
+ // Attach assertion as header via fetch redirect
72
+ await fetch(url.toString(), {
73
+ method: "GET",
74
+ headers: {
75
+ "X-Client-Assertion": assertion,
76
+ },
77
+ credentials: "include",
78
+ }).then((res) => {
79
+ window.location.href = res.url;
80
+ });
101
81
  };
102
82
 
83
+ const login = () => buildRedirect("login");
84
+ const signup = () => buildRedirect("signup");
103
85
 
104
86
  const logout = async () => {
105
- try {
106
- await logoutSession(baseUrl, clientId);
107
- } finally {
108
- setAccessToken(null);
109
- setIsAuthenticated(false);
110
- window.location.href = window.location.origin;
111
- }
87
+ await logoutSession(baseUrl, clientId);
88
+ setAccessToken(null);
89
+ setIsAuthenticated(false);
90
+ window.location.href = window.location.origin;
112
91
  };
113
92
 
114
93
  const value = useMemo(
@@ -124,7 +103,6 @@ export function AutherrProvider({
124
103
  [accessToken, isAuthenticated]
125
104
  );
126
105
 
127
-
128
106
  return (
129
107
  <AutherrContext.Provider value={value}>
130
108
  {children}