@shad-claiborne/hono-middleware-oidc 1.0.9 → 1.1.0
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 +14 -5
- package/dist/index.d.ts +20 -2
- package/dist/index.js +73 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,15 +15,24 @@
|
|
|
15
15
|
```
|
|
16
16
|
### Usage
|
|
17
17
|
```ts
|
|
18
|
-
|
|
18
|
+
import {addIdentity, receiveAuth, handleFlow} from '@shad-claiborne/hono-middleware-oidc'
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
...
|
|
21
21
|
|
|
22
|
+
// Add the identity claims to the request for all routes except the redirect URI
|
|
23
|
+
app.use('*', except('/auth/callback', addIdentity));
|
|
24
|
+
|
|
25
|
+
// The endpoint to which the authorization server provides the user's authorization
|
|
26
|
+
app.use('/auth/callback', receiveAuth);
|
|
27
|
+
|
|
28
|
+
// Send the browser to this endpoint to begin the authorization flow
|
|
29
|
+
// Note this endpoint assumes "addIdentity" precedes it
|
|
30
|
+
app.use('/login', handleFlow);
|
|
31
|
+
|
|
32
|
+
// Example ajax endpoint for getting the identity claims
|
|
22
33
|
app.get('/async/api/identity', async (c) => {
|
|
23
34
|
return c.json(c.get('identity'));
|
|
24
35
|
});
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
return c.text('hello world');
|
|
28
|
-
});
|
|
37
|
+
...
|
|
29
38
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { MiddlewareHandler } from "hono";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* handleFlow
|
|
4
|
+
* @param c
|
|
5
|
+
* @param next
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export declare const handleFlow: MiddlewareHandler;
|
|
9
|
+
/**
|
|
10
|
+
* addIdentity
|
|
11
|
+
* @param c
|
|
12
|
+
* @param next
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export declare const addIdentity: MiddlewareHandler;
|
|
16
|
+
/**
|
|
17
|
+
* receiveAuth
|
|
18
|
+
* @param c
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
export declare const receiveAuth: MiddlewareHandler;
|
|
4
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,64 @@ import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
|
|
|
2
2
|
import { createIdentityProvider, } from "@shad-claiborne/basic-oidc";
|
|
3
3
|
import randomstring from "randomstring";
|
|
4
4
|
import { env } from "hono/adapter";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* activateToken
|
|
7
|
+
* @param c
|
|
8
|
+
* @param tokenRes
|
|
9
|
+
*/
|
|
10
|
+
const activateToken = async (c, provider, tokenResponse) => {
|
|
11
|
+
const { HONO_OIDC_COOKIE_SECRET, HONO_OIDC_ACCESS_TOKEN_COOKIE, HONO_OIDC_REFRESH_TOKEN_COOKIE, HONO_OIDC_ID_TOKEN_COOKIE, } = env(c);
|
|
12
|
+
if (tokenResponse.access_token) {
|
|
13
|
+
await setSignedCookie(c, HONO_OIDC_ACCESS_TOKEN_COOKIE, tokenResponse.access_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge: tokenResponse.expires_in });
|
|
14
|
+
}
|
|
15
|
+
if (tokenResponse.refresh_token) {
|
|
16
|
+
const maxAge = tokenResponse.refresh_token_expires_in ?? (24 * 60 * 60);
|
|
17
|
+
await setSignedCookie(c, HONO_OIDC_REFRESH_TOKEN_COOKIE, tokenResponse.refresh_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
18
|
+
}
|
|
19
|
+
if (tokenResponse.id_token) {
|
|
20
|
+
const idToken = await provider.decodeIdentityToken(tokenResponse.id_token);
|
|
21
|
+
const maxAge = idToken.exp - Math.floor(Date.now() / 1000);
|
|
22
|
+
await setSignedCookie(c, HONO_OIDC_ID_TOKEN_COOKIE, tokenResponse.id_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* handleFlow
|
|
27
|
+
* @param c
|
|
28
|
+
* @param next
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
export const handleFlow = async (c, next) => {
|
|
32
|
+
const { HONO_OIDC_ISSUER, HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET, HONO_OIDC_REDIRECT_URI, HONO_OIDC_COOKIE_SECRET, HONO_OIDC_CODE_VERIFIER_COOKIE, } = env(c);
|
|
33
|
+
const provider = await createIdentityProvider(HONO_OIDC_ISSUER);
|
|
34
|
+
const client = provider.createClient(HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET);
|
|
35
|
+
let id = c.get('identity');
|
|
36
|
+
if (!id) {
|
|
37
|
+
const stateId = randomstring.generate(5);
|
|
38
|
+
const state = { originUrl: c.get('originUrl') || c.req.url };
|
|
39
|
+
await setSignedCookie(c, `_authstate-${stateId}`, JSON.stringify(state), HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax' });
|
|
40
|
+
const codeVerifier = randomstring.generate(16);
|
|
41
|
+
await setSignedCookie(c, HONO_OIDC_CODE_VERIFIER_COOKIE, codeVerifier, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax' });
|
|
42
|
+
const authRequest = client
|
|
43
|
+
.newAuthorizationRequest()
|
|
44
|
+
.setRedirectUri(HONO_OIDC_REDIRECT_URI)
|
|
45
|
+
.setResponseMode("query")
|
|
46
|
+
.setResponseType("code id_token")
|
|
47
|
+
.setScope(["profile"])
|
|
48
|
+
.setCodeChallenge(codeVerifier)
|
|
49
|
+
.setState(stateId);
|
|
50
|
+
const authRequestURL = authRequest.toURL();
|
|
51
|
+
return c.redirect(authRequestURL.toString());
|
|
52
|
+
}
|
|
53
|
+
await next();
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* addIdentity
|
|
57
|
+
* @param c
|
|
58
|
+
* @param next
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export const addIdentity = async (c, next) => {
|
|
62
|
+
const { HONO_OIDC_ISSUER, HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET, HONO_OIDC_COOKIE_SECRET, HONO_OIDC_ACCESS_TOKEN_COOKIE, HONO_OIDC_REFRESH_TOKEN_COOKIE, HONO_OIDC_ID_TOKEN_COOKIE, } = env(c);
|
|
7
63
|
const provider = await createIdentityProvider(HONO_OIDC_ISSUER);
|
|
8
64
|
const client = provider.createClient(HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET);
|
|
9
65
|
const tokenSet = {
|
|
@@ -16,48 +72,30 @@ export const withIdentity = async (c, next) => {
|
|
|
16
72
|
id = await provider.getIdentity(tokenSet);
|
|
17
73
|
}
|
|
18
74
|
catch (err) {
|
|
19
|
-
|
|
75
|
+
console.error(err);
|
|
20
76
|
}
|
|
21
|
-
if (id
|
|
77
|
+
if (!id) {
|
|
22
78
|
try {
|
|
23
79
|
const tokenResponse = await client.refreshAccess(tokenSet);
|
|
24
80
|
id = await provider.getIdentity(tokenResponse);
|
|
25
|
-
|
|
26
|
-
await setSignedCookie(c, HONO_OIDC_ACCESS_TOKEN_COOKIE, tokenResponse.access_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax' });
|
|
27
|
-
}
|
|
28
|
-
if (tokenResponse.refresh_token) {
|
|
29
|
-
const maxAge = tokenResponse.refresh_token_expires_in ?? (24 * 60 * 60);
|
|
30
|
-
await setSignedCookie(c, HONO_OIDC_REFRESH_TOKEN_COOKIE, tokenResponse.refresh_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
31
|
-
}
|
|
32
|
-
if (tokenResponse.id_token) {
|
|
33
|
-
const idToken = await provider.decodeIdentityToken(tokenResponse.id_token);
|
|
34
|
-
const maxAge = idToken.exp - Math.floor(Date.now() / 1000);
|
|
35
|
-
await setSignedCookie(c, HONO_OIDC_ID_TOKEN_COOKIE, tokenResponse.id_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
36
|
-
}
|
|
81
|
+
await activateToken(c, provider, tokenResponse);
|
|
37
82
|
}
|
|
38
83
|
catch (err) {
|
|
39
|
-
|
|
40
|
-
const state = { originUrl: c.get('originUrl') || c.req.url };
|
|
41
|
-
await setSignedCookie(c, `_authstate-${stateId}`, JSON.stringify(state), HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax' });
|
|
42
|
-
const codeVerifier = randomstring.generate(16);
|
|
43
|
-
await setSignedCookie(c, HONO_OIDC_CODE_VERIFIER_COOKIE, codeVerifier, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax' });
|
|
44
|
-
const authRequest = client
|
|
45
|
-
.newAuthorizationRequest()
|
|
46
|
-
.setRedirectUri(HONO_OIDC_REDIRECT_URI)
|
|
47
|
-
.setResponseMode("query")
|
|
48
|
-
.setResponseType("code id_token")
|
|
49
|
-
.setScope(["profile"])
|
|
50
|
-
.setCodeChallenge(codeVerifier)
|
|
51
|
-
.setState(stateId);
|
|
52
|
-
const authRequestURL = authRequest.toURL();
|
|
53
|
-
return c.redirect(authRequestURL.toString());
|
|
84
|
+
console.error(err);
|
|
54
85
|
}
|
|
55
86
|
}
|
|
56
|
-
|
|
87
|
+
if (id) {
|
|
88
|
+
c.set("identity", id);
|
|
89
|
+
}
|
|
57
90
|
await next();
|
|
58
91
|
};
|
|
59
|
-
|
|
60
|
-
|
|
92
|
+
/**
|
|
93
|
+
* receiveAuth
|
|
94
|
+
* @param c
|
|
95
|
+
* @returns
|
|
96
|
+
*/
|
|
97
|
+
export const receiveAuth = async (c) => {
|
|
98
|
+
const { HONO_OIDC_ISSUER, HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET, HONO_OIDC_REDIRECT_URI, HONO_OIDC_COOKIE_SECRET, HONO_OIDC_CODE_VERIFIER_COOKIE, } = env(c);
|
|
61
99
|
const provider = await createIdentityProvider(HONO_OIDC_ISSUER);
|
|
62
100
|
const client = provider.createClient(HONO_OIDC_CLIENT_ID, HONO_OIDC_CLIENT_SECRET);
|
|
63
101
|
const requestURL = new URL(c.req.url), requestParams = requestURL.searchParams;
|
|
@@ -76,18 +114,7 @@ export const forAuthorization = async (c) => {
|
|
|
76
114
|
if (codeVerifier === false || codeVerifier === undefined)
|
|
77
115
|
throw new Error("code verifier cookie failed");
|
|
78
116
|
const tokenResponse = await client.requestAccess(authResponse, { codeVerifier, redirectUri: HONO_OIDC_REDIRECT_URI });
|
|
79
|
-
|
|
80
|
-
await setSignedCookie(c, HONO_OIDC_ACCESS_TOKEN_COOKIE, tokenResponse.access_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge: tokenResponse.expires_in });
|
|
81
|
-
}
|
|
82
|
-
if (tokenResponse.refresh_token) {
|
|
83
|
-
const maxAge = tokenResponse.refresh_token_expires_in ?? (24 * 60 * 60);
|
|
84
|
-
await setSignedCookie(c, HONO_OIDC_REFRESH_TOKEN_COOKIE, tokenResponse.refresh_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
85
|
-
}
|
|
86
|
-
if (tokenResponse.id_token) {
|
|
87
|
-
const idToken = await provider.decodeIdentityToken(tokenResponse.id_token);
|
|
88
|
-
const maxAge = idToken.exp - Math.floor(Date.now() / 1000);
|
|
89
|
-
await setSignedCookie(c, HONO_OIDC_ID_TOKEN_COOKIE, tokenResponse.id_token, HONO_OIDC_COOKIE_SECRET, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge });
|
|
90
|
-
}
|
|
117
|
+
await activateToken(c, provider, tokenResponse);
|
|
91
118
|
return c.redirect(state.originUrl);
|
|
92
119
|
};
|
|
93
120
|
//# sourceMappingURL=index.js.map
|