next-auth-heksso 1.0.7 → 1.0.9
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/api/nextAuthConfig.d.ts +1 -1
- package/api/nextAuthConfig.js +50 -52
- package/package.json +1 -1
- package/react/KeycloakSessionContext.d.ts +1 -0
- package/react/KeycloakSessionContext.js +6 -2
- package/src/api/nextAuthConfig.ts +58 -49
- package/src/react/KeycloakSessionContext.tsx +6 -2
- package/tsconfig.json +1 -1
package/api/nextAuthConfig.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import { NextAuthOptions } from "next-auth";
|
|
|
2
2
|
/**
|
|
3
3
|
* Provides authOptions for next-auth that configures it for use with the typical HEKsso setup
|
|
4
4
|
*/
|
|
5
|
-
export declare
|
|
5
|
+
export declare function configureAuthOptions(options?: NextAuthOptions): NextAuthOptions;
|
package/api/nextAuthConfig.js
CHANGED
|
@@ -12,61 +12,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
15
|
+
exports.configureAuthOptions = void 0;
|
|
16
16
|
const keycloak_1 = __importDefault(require("next-auth/providers/keycloak"));
|
|
17
17
|
const refreshAccessToken_1 = require("./refreshAccessToken");
|
|
18
18
|
/**
|
|
19
19
|
* Provides authOptions for next-auth that configures it for use with the typical HEKsso setup
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
clientId: process.env.KEYCLOAK_CLIENT_ID || "",
|
|
26
|
-
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET || "",
|
|
27
|
-
issuer: process.env.KEYCLOAK_ISSUER,
|
|
28
|
-
authorization: { params: { scope: "openid email profile roles" } }
|
|
29
|
-
})
|
|
30
|
-
],
|
|
31
|
-
pages: {
|
|
32
|
-
signIn: "/",
|
|
33
|
-
},
|
|
34
|
-
callbacks: {
|
|
35
|
-
jwt(data) {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
const hekGroups = (_a = data.profile) === null || _a === void 0 ? void 0 : _a.hekGroups;
|
|
39
|
-
// Persist the OAuth access_token to the token right after signin
|
|
40
|
-
if (data.account && data.user) {
|
|
41
|
-
data.token.idToken = (_b = data.account) === null || _b === void 0 ? void 0 : _b.id_token;
|
|
42
|
-
data.token.accessToken = data.account.access_token;
|
|
43
|
-
data.token.hekGroups = hekGroups;
|
|
44
|
-
data.token.username = data.profile.preferred_username;
|
|
45
|
-
if (data.account.expires_at)
|
|
46
|
-
data.token.accessTokenExpires = data.account.expires_at * 1000;
|
|
47
|
-
data.token.refreshToken = data.account.refresh_token;
|
|
48
|
-
return data.token;
|
|
49
|
-
}
|
|
50
|
-
// Return previous token if the access token has not expired yet
|
|
51
|
-
if (Date.now() < data.token.accessTokenExpires) {
|
|
52
|
-
return data.token;
|
|
53
|
-
}
|
|
54
|
-
// Access token has expired, try to update it
|
|
55
|
-
return (0, refreshAccessToken_1.refreshAccessToken)(data.token);
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
session({ session, token }) {
|
|
59
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
-
// Send properties to the client, like an access_token from a provider.
|
|
61
|
-
const _session = session;
|
|
62
|
-
_session.accessToken = token.accessToken;
|
|
63
|
-
_session.accessTokenExpires = token.accessTokenExpires;
|
|
64
|
-
_session.hekGroups = token.hekGroups || [];
|
|
65
|
-
_session.username = token.username;
|
|
66
|
-
if (token.error)
|
|
67
|
-
_session.error = token.error;
|
|
68
|
-
return _session;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
21
|
+
function configureAuthOptions(options) {
|
|
22
|
+
let callbacks = undefined;
|
|
23
|
+
if (options && "callbacks" in options) {
|
|
24
|
+
callbacks = options.callbacks;
|
|
71
25
|
}
|
|
72
|
-
}
|
|
26
|
+
return Object.assign(Object.assign({}, options), { secret: process.env.NEXTAUTH_SECRET, providers: [
|
|
27
|
+
(0, keycloak_1.default)({
|
|
28
|
+
clientId: process.env.KEYCLOAK_CLIENT_ID || "",
|
|
29
|
+
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET || "",
|
|
30
|
+
issuer: process.env.KEYCLOAK_ISSUER,
|
|
31
|
+
authorization: { params: { scope: "openid email profile roles" } }
|
|
32
|
+
})
|
|
33
|
+
], callbacks: Object.assign({ jwt(data) {
|
|
34
|
+
var _a, _b;
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const hekGroups = (_a = data.profile) === null || _a === void 0 ? void 0 : _a.hekGroups;
|
|
37
|
+
// Persist the OAuth access_token to the token right after signin
|
|
38
|
+
if (data.account && data.user) {
|
|
39
|
+
data.token.idToken = (_b = data.account) === null || _b === void 0 ? void 0 : _b.id_token;
|
|
40
|
+
data.token.accessToken = data.account.access_token;
|
|
41
|
+
data.token.hekGroups = hekGroups;
|
|
42
|
+
data.token.username = data.profile.preferred_username;
|
|
43
|
+
if (data.account.expires_at)
|
|
44
|
+
data.token.accessTokenExpires = data.account.expires_at * 1000;
|
|
45
|
+
data.token.refreshToken = data.account.refresh_token;
|
|
46
|
+
return data.token;
|
|
47
|
+
}
|
|
48
|
+
// Return previous token if the access token has not expired yet
|
|
49
|
+
if (Date.now() < data.token.accessTokenExpires) {
|
|
50
|
+
return data.token;
|
|
51
|
+
}
|
|
52
|
+
// Access token has expired, try to update it
|
|
53
|
+
return (0, refreshAccessToken_1.refreshAccessToken)(data.token);
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
session({ session, token }) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
// Send properties to the client, like an access_token from a provider.
|
|
59
|
+
const _session = session;
|
|
60
|
+
_session.accessToken = token.accessToken;
|
|
61
|
+
_session.accessTokenExpires = token.accessTokenExpires;
|
|
62
|
+
_session.hekGroups = token.hekGroups || [];
|
|
63
|
+
_session.username = token.username;
|
|
64
|
+
if (token.error)
|
|
65
|
+
_session.error = token.error;
|
|
66
|
+
return _session;
|
|
67
|
+
});
|
|
68
|
+
} }, callbacks) });
|
|
69
|
+
}
|
|
70
|
+
exports.configureAuthOptions = configureAuthOptions;
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ export interface KeycloakSession {
|
|
|
5
5
|
getAccessToken: () => Promise<string>;
|
|
6
6
|
}
|
|
7
7
|
export declare const KeycloakSessionContext: React.Context<KeycloakSession>;
|
|
8
|
+
export declare function useKeycloakSession(): KeycloakSession;
|
|
8
9
|
export declare function KeycloakSessionProvider(props: {
|
|
9
10
|
children: ReactNode | ReactNode[];
|
|
10
11
|
signInPage?: string;
|
|
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.KeycloakSessionProvider = exports.KeycloakSessionContext = void 0;
|
|
35
|
+
exports.KeycloakSessionProvider = exports.useKeycloakSession = exports.KeycloakSessionContext = void 0;
|
|
36
36
|
const react_1 = require("next-auth/react");
|
|
37
37
|
const router_1 = require("next/router");
|
|
38
38
|
const react_2 = __importStar(require("react"));
|
|
@@ -43,6 +43,10 @@ exports.KeycloakSessionContext = (0, react_2.createContext)({
|
|
|
43
43
|
return "";
|
|
44
44
|
})
|
|
45
45
|
});
|
|
46
|
+
function useKeycloakSession() {
|
|
47
|
+
return (0, react_2.useContext)(exports.KeycloakSessionContext);
|
|
48
|
+
}
|
|
49
|
+
exports.useKeycloakSession = useKeycloakSession;
|
|
46
50
|
function refreshAccessToken() {
|
|
47
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
48
52
|
try {
|
|
@@ -106,7 +110,7 @@ function KeycloakSessionProvider(props) {
|
|
|
106
110
|
// we check for a valid refresh token here
|
|
107
111
|
(0, react_2.useEffect)(() => {
|
|
108
112
|
var _a, _b, _c, _d;
|
|
109
|
-
if (!router.asPath.
|
|
113
|
+
if (!(router.asPath.split("?")[0] === (props.signInPage || "/auth/signin")) &&
|
|
110
114
|
(session.status === "unauthenticated" || accessTokenError)) {
|
|
111
115
|
router.push(props.signInPage || "/auth/signin");
|
|
112
116
|
return;
|
|
@@ -1,58 +1,67 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { NextAuthOptions } from "next-auth"
|
|
2
2
|
import KeycloakProvider from "next-auth/providers/keycloak"
|
|
3
3
|
import { refreshAccessToken } from "./refreshAccessToken"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Provides authOptions for next-auth that configures it for use with the typical HEKsso setup
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
data.token.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
8
|
+
export function configureAuthOptions(options?: NextAuthOptions): NextAuthOptions {
|
|
9
|
+
let callbacks: NextAuthOptions["callbacks"] | undefined = undefined
|
|
10
|
+
|
|
11
|
+
if (options && "callbacks" in options) {
|
|
12
|
+
callbacks = options.callbacks
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
...options,
|
|
17
|
+
secret: process.env.NEXTAUTH_SECRET,
|
|
18
|
+
providers: [
|
|
19
|
+
KeycloakProvider({
|
|
20
|
+
clientId: process.env.KEYCLOAK_CLIENT_ID || "",
|
|
21
|
+
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET || "",
|
|
22
|
+
issuer: process.env.KEYCLOAK_ISSUER, // something like "http://localhost:8080/realms/master"
|
|
23
|
+
authorization: { params: { scope: "openid email profile roles" } }
|
|
24
|
+
})
|
|
25
|
+
],
|
|
26
|
+
callbacks: {
|
|
27
|
+
async jwt(data) {
|
|
28
|
+
const hekGroups = (data.profile as any)?.hekGroups
|
|
29
|
+
|
|
30
|
+
// Persist the OAuth access_token to the token right after signin
|
|
31
|
+
if (data.account && data.user) {
|
|
32
|
+
data.token.idToken = data.account?.id_token
|
|
33
|
+
data.token.accessToken = data.account.access_token
|
|
34
|
+
data.token.hekGroups = hekGroups
|
|
35
|
+
data.token.username = (
|
|
36
|
+
data.profile as { [key: string]: string }
|
|
37
|
+
).preferred_username
|
|
38
|
+
|
|
39
|
+
if (data.account.expires_at)
|
|
40
|
+
data.token.accessTokenExpires = data.account.expires_at * 1000
|
|
41
|
+
|
|
42
|
+
data.token.refreshToken = data.account.refresh_token
|
|
43
|
+
return data.token
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Return previous token if the access token has not expired yet
|
|
47
|
+
if (Date.now() < (data.token.accessTokenExpires as number)) {
|
|
48
|
+
return data.token
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Access token has expired, try to update it
|
|
52
|
+
return refreshAccessToken(data.token)
|
|
53
|
+
},
|
|
54
|
+
async session({ session, token }) {
|
|
55
|
+
// Send properties to the client, like an access_token from a provider.
|
|
56
|
+
const _session = session as any
|
|
57
|
+
_session.accessToken = token.accessToken
|
|
58
|
+
_session.accessTokenExpires = token.accessTokenExpires
|
|
59
|
+
_session.hekGroups = token.hekGroups || []
|
|
60
|
+
_session.username = token.username
|
|
61
|
+
if (token.error) _session.error = token.error
|
|
62
|
+
return _session
|
|
63
|
+
},
|
|
64
|
+
...callbacks
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signOut, useSession } from "next-auth/react"
|
|
2
2
|
import { useRouter } from "next/router"
|
|
3
|
-
import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useState } from "react"
|
|
3
|
+
import React, { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from "react"
|
|
4
4
|
|
|
5
5
|
export interface KeycloakSession {
|
|
6
6
|
isAuthenticated: boolean
|
|
@@ -16,6 +16,10 @@ export const KeycloakSessionContext = createContext<KeycloakSession>({
|
|
|
16
16
|
}
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
+
export function useKeycloakSession() {
|
|
20
|
+
return useContext(KeycloakSessionContext)
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
async function refreshAccessToken() {
|
|
20
24
|
try {
|
|
21
25
|
const response = await fetch("/api/auth/session", {
|
|
@@ -83,7 +87,7 @@ export function KeycloakSessionProvider(props: {
|
|
|
83
87
|
// we check for a valid refresh token here
|
|
84
88
|
useEffect(() => {
|
|
85
89
|
if (
|
|
86
|
-
!router.asPath.
|
|
90
|
+
!(router.asPath.split("?")[0] === (props.signInPage || "/auth/signin")) &&
|
|
87
91
|
(session.status === "unauthenticated" || accessTokenError)
|
|
88
92
|
) {
|
|
89
93
|
router.push(props.signInPage || "/auth/signin")
|
package/tsconfig.json
CHANGED
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
101
101
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
102
102
|
},
|
|
103
|
-
"exclude": ["
|
|
103
|
+
"exclude": ["api", "react", "node_modules"]
|
|
104
104
|
}
|