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.
- package/dist/crypto/createClientAssertion.js +7 -6
- package/dist/hooks/useAutherr.d.ts +2 -2
- package/dist/provider/AutherrProvider.d.ts +2 -2
- package/dist/provider/AutherrProvider.js +21 -32
- package/package.json +1 -1
- package/src/crypto/createClientAssertion.ts +7 -7
- package/src/provider/AutherrProvider.tsx +27 -49
|
@@ -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
|
-
|
|
10
|
-
return await new SignJWT({ clientId })
|
|
9
|
+
return await new SignJWT({})
|
|
11
10
|
.setProtectedHeader({ alg: "RS256", typ: "JWT" })
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
.setAudience("autherr")
|
|
15
|
-
.
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
@@ -17,13 +17,13 @@ export async function createClientAssertion(
|
|
|
17
17
|
["sign"]
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return await new SignJWT({ clientId })
|
|
20
|
+
return await new SignJWT({})
|
|
23
21
|
.setProtectedHeader({ alg: "RS256", typ: "JWT" })
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.setAudience("autherr")
|
|
27
|
-
.
|
|
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;
|
|
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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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}
|