@gneiss/client-auth 1.0.1
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 +68 -0
- package/dist/cjs/index.js +352 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/esm/index.js +314 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/types/src/core/AuthGneissCore.d.ts +49 -0
- package/dist/types/src/core/AuthGneissCore.d.ts.map +1 -0
- package/dist/types/src/core/index.d.ts +5 -0
- package/dist/types/src/core/index.d.ts.map +1 -0
- package/dist/types/src/core/types.d.ts +20 -0
- package/dist/types/src/core/types.d.ts.map +1 -0
- package/dist/types/src/frameworks/express/middleware/ExpressAuthGneissClient.d.ts +55 -0
- package/dist/types/src/frameworks/express/middleware/ExpressAuthGneissClient.d.ts.map +1 -0
- package/dist/types/src/frameworks/index.d.ts +3 -0
- package/dist/types/src/frameworks/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/utils/index.d.ts +2 -0
- package/dist/types/src/utils/index.d.ts.map +1 -0
- package/dist/types/src/utils/storage/cookieHandling.d.ts +20 -0
- package/dist/types/src/utils/storage/cookieHandling.d.ts.map +1 -0
- package/dist/types/tests/frameworks/express/middleware/ExpressAuthGneissClient.integration.test.d.ts +2 -0
- package/dist/types/tests/frameworks/express/middleware/ExpressAuthGneissClient.integration.test.d.ts.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# js-gneiss-auth-client
|
|
2
|
+
|
|
3
|
+
A JavaScript client library for integrating with the Gneiss Authentication service. Provides OAuth2 authentication flow with support for access and refresh tokens.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
### Installing from npm registry:
|
|
7
|
+
```bash
|
|
8
|
+
npm install @gneiss/client-auth
|
|
9
|
+
```
|
|
10
|
+
### Installing from bitbucket:
|
|
11
|
+
1. Setup SSH keys:
|
|
12
|
+
* **Windows**: https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-windows/
|
|
13
|
+
* **Linux**: https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/
|
|
14
|
+
* **MacOS**: https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-macos/
|
|
15
|
+
2. Install the bitbucket npm package via the repository using git+ssh:
|
|
16
|
+
```bash
|
|
17
|
+
npm install git+ssh://git@bitbucket.org:gneissrev/js-client-auth-gneiss.git<branch>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
Example for an Express.js application:
|
|
22
|
+
```typescript
|
|
23
|
+
import express from 'express';
|
|
24
|
+
import { ExpressAuthGneissClient } from "client-auth-gneiss";
|
|
25
|
+
import { AuthGneissCoreConfig } from 'client-auth-gneiss';
|
|
26
|
+
|
|
27
|
+
// Create express app
|
|
28
|
+
const app = express();
|
|
29
|
+
|
|
30
|
+
// Initialize auth config
|
|
31
|
+
const config: AuthGneissCoreConfig = {
|
|
32
|
+
clientId: "<Your app's client ID>,
|
|
33
|
+
clientSecret: "<Your app's client secret>",
|
|
34
|
+
baseUrl: "<Your app's base url>",
|
|
35
|
+
redirectUrl: "/callback"
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//Instantiate the auth client with config
|
|
39
|
+
const auth = new ExpressAuthGneissClient(config);
|
|
40
|
+
|
|
41
|
+
// Login route - redirects to Gneiss login page
|
|
42
|
+
app.get("/login", auth.login);
|
|
43
|
+
|
|
44
|
+
// Logout route - redirects to Gneiss logout page
|
|
45
|
+
app.get("/logout", auth.logout);
|
|
46
|
+
|
|
47
|
+
// Callback route - handles OAuth callback from Gneiss
|
|
48
|
+
app.get("/callback", auth.handleCallBack);
|
|
49
|
+
|
|
50
|
+
// Protected route example
|
|
51
|
+
app.get("/dashboard", auth.requireAuth, (req, res) => {
|
|
52
|
+
res.send('This is a protected route');
|
|
53
|
+
});
|
|
54
|
+
// Protected user data route
|
|
55
|
+
app.get("/user", auth.requireAuth, auth.getUser);
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
* `ExpressAuthGneissClient` is a class that provides middleware for handling authentication in an Express.js application. Parameters are:
|
|
60
|
+
* `clientId` - This is the client id of your application. This will be provided to you by the appropriate Gneiss team member.
|
|
61
|
+
* `clientSecret` - This is the client secret of your application. This will be provided to you by the appropriate Gneiss team member.
|
|
62
|
+
* `baseUrl` - This is the base url (without any endpoints) of your service/app. Note that this may be different depending on your environment (prod or dev)
|
|
63
|
+
* `redirectUrl` - This is the route that the user will be redirected to for authentication. This should be the same as your callback route.
|
|
64
|
+
* `auth.login` is a function that redirects the user to the Gneiss authentication service for authentication.
|
|
65
|
+
* `auth.handleCallBack` is a function that handles the callback from the Gneiss authentication service. It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.
|
|
66
|
+
* `auth.requireAuth` is a middleware function that checks if the user is authenticated. If the user is not authenticated, it redirects the user to the login page.
|
|
67
|
+
* `auth.getUser` is an included utility route to allow one to retrieve the current user's data. It is important to include the `auth.requireAuth` middleware to ensure the access token is present before accessing the user's data. If it is not, an exeption will be thrown.
|
|
68
|
+
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AuthGneissCore: () => AuthGneissCore_default,
|
|
34
|
+
ExpressAuthGneissClient: () => ExpressAuthGneissClient_default
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/core/AuthGneissCore.ts
|
|
39
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
40
|
+
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
41
|
+
var import_axios2 = require("axios");
|
|
42
|
+
import_dotenv.default.config();
|
|
43
|
+
var AuthGneissCore = class {
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
this.gneissEnpoint = process.env.GNEISS_ENDPOINT;
|
|
47
|
+
this.loginUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/login` : void 0;
|
|
48
|
+
this.logoutUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/logout` : void 0;
|
|
49
|
+
this.invalidateUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/invalidate_token` : void 0;
|
|
50
|
+
let errorMsgs = [];
|
|
51
|
+
if (!process.env.GNEISS_ENDPOINT) {
|
|
52
|
+
errorMsgs.push("GNEISS_ENDPOINT is not set in environment variables");
|
|
53
|
+
}
|
|
54
|
+
if (!process.env.NODE_ENV) {
|
|
55
|
+
errorMsgs.push("NODE_ENV is not set in environment variables");
|
|
56
|
+
}
|
|
57
|
+
if (errorMsgs.length > 0) {
|
|
58
|
+
throw new Error(errorMsgs.join("\n"));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* getTokens is a method that exchanges an authentication code for access and refresh tokens.
|
|
63
|
+
* The client id and secret are passed as basic auth headers to authenticate the client itself.
|
|
64
|
+
* @param authCode - The authentication code received from the Gneiss authentication service.
|
|
65
|
+
* @returns A promise that resolves to an object containing the access and refresh tokens.
|
|
66
|
+
*/
|
|
67
|
+
async getTokens(authCode) {
|
|
68
|
+
try {
|
|
69
|
+
const url = `${this.gneissEnpoint}/auth/access_token?auth_code=${authCode}`;
|
|
70
|
+
const encodedClientId = btoa(this.config.clientId);
|
|
71
|
+
const encodedClientSecret = btoa(this.config.clientSecret);
|
|
72
|
+
const response = await import_axios.default.post(url, {}, {
|
|
73
|
+
headers: {
|
|
74
|
+
"Authorization": `Basic ${encodedClientId}:${encodedClientSecret}`
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
accessToken: response.data.access_token,
|
|
79
|
+
refreshToken: response.data.refresh_token,
|
|
80
|
+
tokenType: response.data.token_type
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* refreshToken is a method that refreshes the access token using the refresh token.
|
|
88
|
+
* @param refreshToken - The refresh token to be used for token refresh.
|
|
89
|
+
* @returns A promise that resolves to the refreshed access token.
|
|
90
|
+
*/
|
|
91
|
+
async refreshToken(refreshToken) {
|
|
92
|
+
try {
|
|
93
|
+
const url = `${this.gneissEnpoint}/auth/refresh`;
|
|
94
|
+
const response = await import_axios.default.post(url, {
|
|
95
|
+
headers: {
|
|
96
|
+
"Authorization": `Bearer ${refreshToken}`
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return response.data.accessToken;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* getUserData is a method that fetches user data using the access token.
|
|
106
|
+
* @param accessToken - The access token to be used for user data fetching.
|
|
107
|
+
* @returns A promise that resolves to the user data.
|
|
108
|
+
*/
|
|
109
|
+
async getUserData(accessToken) {
|
|
110
|
+
const url = `${this.gneissEnpoint}/resource/user_data`;
|
|
111
|
+
const response = await import_axios.default.get(url, {
|
|
112
|
+
headers: {
|
|
113
|
+
"Authorization": `Bearer ${accessToken}`
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
if (response.status === 200) {
|
|
117
|
+
return response.data;
|
|
118
|
+
}
|
|
119
|
+
throw new Error("Failed to fetch user data");
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* validateToken is a method that validates the access token.
|
|
123
|
+
* @param token - The access token to be validated.
|
|
124
|
+
* @returns A promise that resolves to a boolean indicating the validity of the token.
|
|
125
|
+
*/
|
|
126
|
+
async validateToken(token) {
|
|
127
|
+
try {
|
|
128
|
+
console.log("DEBUG: token", token);
|
|
129
|
+
if (!token) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const url = `${this.gneissEnpoint}/auth/validate_token`;
|
|
133
|
+
const response = await import_axios.default.get(url, {
|
|
134
|
+
headers: {
|
|
135
|
+
"Authorization": `Bearer ${token}`
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return response.status === 200;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (error instanceof import_axios2.AxiosError && error.response?.status === 401) {
|
|
141
|
+
return false;
|
|
142
|
+
} else {
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* getLoginUrl is a method that returns the login URL.
|
|
149
|
+
* @returns The login URL.
|
|
150
|
+
*/
|
|
151
|
+
getLoginUrl() {
|
|
152
|
+
return this.loginUrl;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var AuthGneissCore_default = AuthGneissCore;
|
|
156
|
+
|
|
157
|
+
// src/utils/storage/cookieHandling.ts
|
|
158
|
+
var import_jsonwebtoken = require("jsonwebtoken");
|
|
159
|
+
function setAccessToken(res, accessToken) {
|
|
160
|
+
const decodedToken = (0, import_jsonwebtoken.decode)(accessToken);
|
|
161
|
+
if (!decodedToken.exp) {
|
|
162
|
+
throw new Error("Access token does not contain an expiration time");
|
|
163
|
+
}
|
|
164
|
+
res.cookie("accessToken", accessToken, {
|
|
165
|
+
httpOnly: true,
|
|
166
|
+
secure: process.env.NODE_ENV === "production",
|
|
167
|
+
sameSite: "strict",
|
|
168
|
+
maxAge: decodedToken.exp * 1e3 - Date.now()
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function setRefreshToken(res, refreshToken) {
|
|
172
|
+
const decodedToken = (0, import_jsonwebtoken.decode)(refreshToken);
|
|
173
|
+
if (!decodedToken.exp) {
|
|
174
|
+
throw new Error("Refresh token does not contain an expiration time");
|
|
175
|
+
}
|
|
176
|
+
res.cookie("refreshToken", refreshToken, {
|
|
177
|
+
httpOnly: true,
|
|
178
|
+
secure: process.env.NODE_ENV === "production",
|
|
179
|
+
sameSite: "strict",
|
|
180
|
+
maxAge: decodedToken.exp * 1e3 - Date.now()
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function parseCookies(req) {
|
|
184
|
+
const cookies = req.headers.cookie;
|
|
185
|
+
if (!cookies) {
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
return cookies.split(";").reduce((acc, cookie) => {
|
|
189
|
+
const [key, value] = cookie.split("=").map((s) => s.trim());
|
|
190
|
+
acc[key] = value;
|
|
191
|
+
return acc;
|
|
192
|
+
}, {});
|
|
193
|
+
}
|
|
194
|
+
function clearCookies(res) {
|
|
195
|
+
res.clearCookie("accessToken", {
|
|
196
|
+
httpOnly: true,
|
|
197
|
+
secure: process.env.NODE_ENV === "production",
|
|
198
|
+
sameSite: "strict",
|
|
199
|
+
path: "/"
|
|
200
|
+
});
|
|
201
|
+
res.clearCookie("refreshToken", {
|
|
202
|
+
httpOnly: true,
|
|
203
|
+
secure: process.env.NODE_ENV === "production",
|
|
204
|
+
sameSite: "strict",
|
|
205
|
+
path: "/"
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/frameworks/express/middleware/ExpressAuthGneissClient.ts
|
|
210
|
+
var import_axios3 = require("axios");
|
|
211
|
+
var import_axios4 = __toESM(require("axios"), 1);
|
|
212
|
+
var ExpressAuthGneissClient = class extends AuthGneissCore_default {
|
|
213
|
+
constructor(config) {
|
|
214
|
+
super(config);
|
|
215
|
+
this.requireAuth = this.requireAuth.bind(this);
|
|
216
|
+
this.handleCallBack = this.handleCallBack.bind(this);
|
|
217
|
+
this.login = this.login.bind(this);
|
|
218
|
+
this.logout = this.logout.bind(this);
|
|
219
|
+
this.getUser = this.getUser.bind(this);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* requireAuth is a middleware function that checks if the access token is valid.
|
|
223
|
+
* If the access token is not valid, it attempts to refresh the token using the refresh token.
|
|
224
|
+
* If the refresh token is not valid, it redirects the user to the login page.
|
|
225
|
+
* @param req - The request object.
|
|
226
|
+
* @param res - The response object.
|
|
227
|
+
* @param next - The next middleware function.
|
|
228
|
+
*/
|
|
229
|
+
async requireAuth(req, res, next) {
|
|
230
|
+
const cookies = parseCookies(req);
|
|
231
|
+
console.log("DEBUG: cookies", cookies);
|
|
232
|
+
try {
|
|
233
|
+
const isAccessTokenValid = await this.validateToken(cookies?.accessToken);
|
|
234
|
+
if (!isAccessTokenValid) {
|
|
235
|
+
const newAccessToken = await this.refreshToken(cookies?.refreshToken);
|
|
236
|
+
if (newAccessToken) {
|
|
237
|
+
setAccessToken(res, newAccessToken);
|
|
238
|
+
} else {
|
|
239
|
+
const returnToUrl = req.originalUrl;
|
|
240
|
+
res.redirect(`${this.loginUrl}?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}&return_to_url=${returnToUrl}`);
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
next();
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (error instanceof import_axios3.AxiosError) {
|
|
247
|
+
res.status(error.response?.status || 500).send(error.response?.data || "Internal server error");
|
|
248
|
+
} else {
|
|
249
|
+
res.status(500).send("Internal server error");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* getUserData is a middleware function that fetches user data using the access token.
|
|
255
|
+
* @param req - The request object.
|
|
256
|
+
* @param res - The response object.
|
|
257
|
+
* @param next - The next middleware function.
|
|
258
|
+
*/
|
|
259
|
+
async getUser(req, res) {
|
|
260
|
+
const cookies = parseCookies(req);
|
|
261
|
+
const accessToken = cookies?.accessToken;
|
|
262
|
+
if (!accessToken) {
|
|
263
|
+
throw new Error("No access token found in request cookies");
|
|
264
|
+
}
|
|
265
|
+
const userData = await this.getUserData(accessToken);
|
|
266
|
+
res.status(200).send(userData);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* handleCallBack is a middleware function that handles the callback from the authentication service.
|
|
270
|
+
* It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.
|
|
271
|
+
* @param req - The request object.
|
|
272
|
+
* @param res - The response object.
|
|
273
|
+
* @param next - The next middleware function.
|
|
274
|
+
*/
|
|
275
|
+
async handleCallBack(req, res, next) {
|
|
276
|
+
try {
|
|
277
|
+
const authCode = req.query.auth_code;
|
|
278
|
+
const returnToUrl = req.query.return_to_url;
|
|
279
|
+
if (!authCode) {
|
|
280
|
+
throw new Error("No auth code found in request url parameters");
|
|
281
|
+
}
|
|
282
|
+
const tokens = await this.getTokens(authCode);
|
|
283
|
+
setAccessToken(res, tokens.accessToken);
|
|
284
|
+
setRefreshToken(res, tokens.refreshToken);
|
|
285
|
+
if (returnToUrl) {
|
|
286
|
+
res.redirect(returnToUrl);
|
|
287
|
+
} else {
|
|
288
|
+
res.redirect("/");
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (error instanceof import_axios3.AxiosError) {
|
|
292
|
+
res.status(error.response?.status || 500).send(error.response?.data || "Internal server error");
|
|
293
|
+
console.error("DEBUG: error", error);
|
|
294
|
+
} else {
|
|
295
|
+
res.status(500).send("Internal server error");
|
|
296
|
+
console.error("DEBUG: error", error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* login is a function that redirects the user to the Gneiss authentication service for authentication.
|
|
302
|
+
* @param req - The request object.
|
|
303
|
+
* @param res - The response object.
|
|
304
|
+
*/
|
|
305
|
+
login(req, res) {
|
|
306
|
+
try {
|
|
307
|
+
if (!this.loginUrl) {
|
|
308
|
+
throw new Error("Login URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
309
|
+
}
|
|
310
|
+
res.redirect(this.loginUrl + `?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}`);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error("Error in login middleware:", error);
|
|
313
|
+
res.status(500).send("Internal server error");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* logout is a function that redirects the user to the Gneiss logout service.
|
|
318
|
+
* @param req - The request object.
|
|
319
|
+
* @param res - The response object.
|
|
320
|
+
*/
|
|
321
|
+
logout(req, res) {
|
|
322
|
+
const cookies = parseCookies(req);
|
|
323
|
+
try {
|
|
324
|
+
if (!this.logoutUrl) {
|
|
325
|
+
throw new Error("Logout URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
326
|
+
}
|
|
327
|
+
if (!this.invalidateUrl) {
|
|
328
|
+
throw new Error("Invalidate URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
329
|
+
}
|
|
330
|
+
if (cookies?.accessToken) {
|
|
331
|
+
import_axios4.default.post(this.invalidateUrl, {}, {
|
|
332
|
+
// Invalidate the access token
|
|
333
|
+
headers: {
|
|
334
|
+
"Authorization": `Bearer ${cookies?.accessToken}`
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
clearCookies(res);
|
|
339
|
+
res.redirect(this.logoutUrl + `?redirect_url=${this.config.baseUrl}`);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error("Error in logout middleware:", error);
|
|
342
|
+
res.status(500).send("Internal server error");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var ExpressAuthGneissClient_default = ExpressAuthGneissClient;
|
|
347
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
348
|
+
0 && (module.exports = {
|
|
349
|
+
AuthGneissCore,
|
|
350
|
+
ExpressAuthGneissClient
|
|
351
|
+
});
|
|
352
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts", "../../src/core/AuthGneissCore.ts", "../../src/utils/storage/cookieHandling.ts", "../../src/frameworks/express/middleware/ExpressAuthGneissClient.ts"],
|
|
4
|
+
"sourcesContent": ["export { ExpressAuthGneissClient } from \"./frameworks\";\r\nexport { AuthGneissCore } from \"./core\";\r\nexport type { AuthGneissCoreConfig } from \"./core\";\r\n", "import { AuthGneissCoreConfig } from \"@core/types\";\r\nimport axios, { AxiosResponse } from \"axios\";\r\nimport { Tokens } from \"@core/types\";\r\nimport { JwtPayload } from \"jsonwebtoken\";\r\nimport dotenv from \"dotenv\";\r\nimport { AxiosError } from \"axios\";\r\n\r\n//load environment variables\r\ndotenv.config();\r\n\r\n/**\r\n * AuthGneissCore provides core functionality for OAuth2 authentication flow with Gneiss authentication service.\r\n * It handles token exchange, token refresh, user data fetching, and token validation.\r\n * \r\n * This class serves as a base class that can be extended by framework-specific implementations\r\n * to provide authentication middleware and handlers.\r\n */\r\nclass AuthGneissCore {\r\n protected config: AuthGneissCoreConfig; // Configuration object\r\n protected gneissEnpoint : string | undefined\r\n protected loginUrl : string | undefined\r\n protected logoutUrl : string | undefined\r\n protected invalidateUrl : string | undefined\r\n\r\n constructor(\r\n config: AuthGneissCoreConfig\r\n ) {\r\n this.config = config;\r\n this.gneissEnpoint = process.env.GNEISS_ENDPOINT // Gneiss endpoint\r\n this.loginUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/login` : undefined; // Login URL\r\n this.logoutUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/logout` : undefined; // Logout URL\r\n this.invalidateUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/invalidate_token` : undefined; // Invalidate URL\r\n\r\n //check if environment variables are set\r\n let errorMsgs = [];\r\n if (!process.env.GNEISS_ENDPOINT) {\r\n errorMsgs.push(\"GNEISS_ENDPOINT is not set in environment variables\");\r\n }\r\n if (!process.env.NODE_ENV) {\r\n errorMsgs.push(\"NODE_ENV is not set in environment variables\");\r\n }\r\n if (errorMsgs.length > 0) {\r\n throw new Error(errorMsgs.join(\"\\n\"));\r\n }\r\n }\r\n\r\n /**\r\n * getTokens is a method that exchanges an authentication code for access and refresh tokens.\r\n * The client id and secret are passed as basic auth headers to authenticate the client itself.\r\n * @param authCode - The authentication code received from the Gneiss authentication service.\r\n * @returns A promise that resolves to an object containing the access and refresh tokens.\r\n */\r\n async getTokens(authCode : string) : Promise<Tokens> {\r\n try {\r\n const url : string = `${this.gneissEnpoint}/auth/access_token?auth_code=${authCode}`;\r\n //Encode in base64 before transport\r\n const encodedClientId = btoa(this.config.clientId);\r\n const encodedClientSecret = btoa(this.config.clientSecret);\r\n const response : AxiosResponse = await axios.post(url, {}, {\r\n headers: {\r\n \"Authorization\": `Basic ${encodedClientId}:${encodedClientSecret}`\r\n }\r\n });\r\n return {\r\n accessToken: response.data.access_token,\r\n refreshToken: response.data.refresh_token,\r\n tokenType: response.data.token_type\r\n } as Tokens;\r\n } catch (error) {\r\n // console.error(\"Error in getTokens:\", error);\r\n throw error;\r\n }\r\n }\r\n \r\n /**\r\n * refreshToken is a method that refreshes the access token using the refresh token.\r\n * @param refreshToken - The refresh token to be used for token refresh.\r\n * @returns A promise that resolves to the refreshed access token.\r\n */\r\n async refreshToken(refreshToken: string): Promise<string | null> {\r\n try {\r\n const url : string = `${this.gneissEnpoint}/auth/refresh`;\r\n const response : AxiosResponse = await axios.post(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${refreshToken}`\r\n }\r\n });\r\n return response.data.accessToken as string;\r\n } catch (error) {\r\n // console.error(\"Error in refreshToken:\", error);\r\n return null;\r\n }\r\n }\r\n \r\n /**\r\n * getUserData is a method that fetches user data using the access token.\r\n * @param accessToken - The access token to be used for user data fetching.\r\n * @returns A promise that resolves to the user data.\r\n */\r\n async getUserData(accessToken: string) {\r\n const url : string = `${this.gneissEnpoint}/resource/user_data`;\r\n const response : AxiosResponse = await axios.get(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${accessToken}`\r\n }\r\n });\r\n if (response.status === 200) {\r\n return response.data;\r\n }\r\n throw new Error(\"Failed to fetch user data\");\r\n }\r\n \r\n /**\r\n * validateToken is a method that validates the access token.\r\n * @param token - The access token to be validated.\r\n * @returns A promise that resolves to a boolean indicating the validity of the token.\r\n */\r\n async validateToken(token: string): Promise<boolean> {\r\n try {\r\n console.log(\"DEBUG: token\", token);\r\n // Token validation logic\r\n if (!token) {\r\n return false;\r\n }\r\n const url : string = `${this.gneissEnpoint}/auth/validate_token`;\r\n const response : AxiosResponse = await axios.get(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${token}`\r\n }\r\n });\r\n return response.status === 200;\r\n } catch (error) {\r\n // console.error(\"Error in validateToken:\", error);\r\n if (error instanceof AxiosError && error.response?.status === 401) {\r\n return false;\r\n } else {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * getLoginUrl is a method that returns the login URL.\r\n * @returns The login URL.\r\n */\r\n public getLoginUrl() : string | undefined {\r\n return this.loginUrl;\r\n }\r\n}\r\n\r\nexport default AuthGneissCore;\r\n", "import { Response } from \"express\";\r\nimport { JwtPayload, decode } from \"jsonwebtoken\";\r\nimport { Request } from \"express\";\r\n\r\n/**\r\n * Set the access token in the response cookies.\r\n * @param res - The response object.\r\n * @param accessToken - The access token to set.\r\n */\r\nfunction setAccessToken(res: Response, accessToken: string) {\r\n\r\n const decodedToken = decode(accessToken) as JwtPayload;\r\n \r\n // decoded.exp is in seconds since epoch\r\n // Date.now() returns milliseconds since epoch\r\n // maxAge needs milliseconds remaining\r\n if (!decodedToken.exp) {\r\n throw new Error(\"Access token does not contain an expiration time\");\r\n }\r\n \r\n res.cookie('accessToken', accessToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n maxAge: (decodedToken.exp * 1000) - Date.now()\r\n });\r\n}\r\n\r\n/**\r\n * Set the refresh token in the response cookies.\r\n * @param res - The response object.\r\n * @param refreshToken - The refresh token to set.\r\n */\r\nfunction setRefreshToken(res: Response, refreshToken: string) {\r\n\r\n const decodedToken = decode(refreshToken) as JwtPayload;\r\n\r\n if (!decodedToken.exp) {\r\n throw new Error(\"Refresh token does not contain an expiration time\");\r\n }\r\n\r\n res.cookie('refreshToken', refreshToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n maxAge: (decodedToken.exp * 1000) - Date.now()\r\n });\r\n}\r\n\r\nfunction parseCookies(req: Request) : { [key: string]: string } {\r\n const cookies = req.headers.cookie;\r\n if (!cookies) {\r\n return {};\r\n }\r\n return cookies.split(';').reduce((acc: { [key: string]: string }, cookie) => {\r\n const [key, value] = cookie.split('=').map(s => s.trim());\r\n acc[key] = value;\r\n return acc;\r\n }, {});\r\n}\r\n\r\nfunction clearCookies(res: Response) {\r\n res.clearCookie(\"accessToken\", {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/'\r\n });\r\n res.clearCookie(\"refreshToken\", {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/'\r\n });\r\n}\r\n\r\nexport { setAccessToken, setRefreshToken, parseCookies, clearCookies };\r\n", "import { AuthGneissCore, AuthGneissCoreConfig } from \"@core\";\r\nimport { Request, Response, NextFunction } from \"express\";\r\nimport { RequestWithTokens, Tokens } from \"@core/types\";\r\nimport { setAccessToken, setRefreshToken, parseCookies } from \"@utils\";\r\nimport { JwtPayload } from \"jsonwebtoken\";\r\nimport { AxiosError } from \"axios\";\r\nimport axios from \"axios\";\r\nimport { clearCookies } from \"@/utils/storage/cookieHandling\";\r\n\r\n/**\r\n * ExpressAuthGneissClient extends AuthGneissCore to provide Express-specific authentication middleware\r\n * and functionality for handling OAuth2 authentication flow with Gneiss authentication service.\r\n * \r\n * @extends AuthGneissCore\r\n * @example\r\n * const authClient = new ExpressAuthGneissClient({\r\n * clientId: 'your-client-id',\r\n * clientSecret: 'your-client-secret',\r\n * redirectUrl: 'your-redirect-url'\r\n * });\r\n */\r\nclass ExpressAuthGneissClient extends AuthGneissCore {\r\n\r\n constructor(\r\n config: AuthGneissCoreConfig\r\n ) {\r\n super(config);\r\n \r\n // Bind the methods in constructor\r\n this.requireAuth = this.requireAuth.bind(this);\r\n this.handleCallBack = this.handleCallBack.bind(this);\r\n this.login = this.login.bind(this);\r\n this.logout = this.logout.bind(this);\r\n this.getUser = this.getUser.bind(this);\r\n }\r\n\r\n /**\r\n * requireAuth is a middleware function that checks if the access token is valid.\r\n * If the access token is not valid, it attempts to refresh the token using the refresh token.\r\n * If the refresh token is not valid, it redirects the user to the login page.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async requireAuth(req: Request, res: Response, next: NextFunction): Promise<void> {\r\n const cookies = parseCookies(req);\r\n //Check for the existence of the access token\r\n console.log(\"DEBUG: cookies\", cookies);\r\n try {\r\n const isAccessTokenValid : boolean = await this.validateToken(cookies?.accessToken);\r\n if (!isAccessTokenValid) { //if the access token is not valid\r\n //try to refresh the token\r\n const newAccessToken : string | null = await this.refreshToken(cookies?.refreshToken);\r\n if (newAccessToken) { //\r\n setAccessToken(res, newAccessToken);\r\n }\r\n else {\r\n // no access token or valid refresh token, redirect to login\r\n const returnToUrl : string | undefined = req.originalUrl as string;\r\n res.redirect(`${this.loginUrl}?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}&return_to_url=${returnToUrl}`);\r\n }\r\n }\r\n else {\r\n // access token is valid, continue to the next middleware or route handler\r\n next();\r\n }\r\n } catch (error) {\r\n // console.error('Error in requireAuth middleware:', error);\r\n if (error instanceof AxiosError) {\r\n res.status((error as AxiosError).response?.status || 500).send((error as AxiosError).response?.data || 'Internal server error');\r\n } else {\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * getUserData is a middleware function that fetches user data using the access token.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async getUser(req: Request, res: Response): Promise<void> {\r\n const cookies = parseCookies(req);\r\n const accessToken = cookies?.accessToken;\r\n if (!accessToken) {\r\n throw new Error(\"No access token found in request cookies\");\r\n }\r\n const userData = await this.getUserData(accessToken);\r\n res.status(200).send(userData);\r\n }\r\n\r\n /**\r\n * handleCallBack is a middleware function that handles the callback from the authentication service.\r\n * It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async handleCallBack(\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n ): Promise<void> {\r\n try {\r\n const authCode: string | undefined = req.query.auth_code as string\r\n const returnToUrl : string | undefined = req.query.return_to_url as string;\r\n if (!authCode) {\r\n throw new Error(\"No auth code found in request url parameters\");\r\n }\r\n\r\n const tokens: Tokens = await this.getTokens(authCode);\r\n \r\n // Set the access and refresh tokens in the response cookies\r\n setAccessToken(res, tokens.accessToken);\r\n setRefreshToken(res, tokens.refreshToken);\r\n\r\n if (returnToUrl) {\r\n // Go to the original request url\r\n res.redirect(returnToUrl);\r\n }\r\n else {\r\n // Go to the root url\r\n res.redirect(\"/\")\r\n }\r\n } catch (error) {\r\n // console.error('Error in handleCallBack middleware:', error);\r\n if (error instanceof AxiosError) {\r\n res.status((error as AxiosError).response?.status || 500).send((error as AxiosError).response?.data || 'Internal server error');\r\n console.error(\"DEBUG: error\", error);\r\n } else {\r\n res.status(500).send('Internal server error');\r\n console.error(\"DEBUG: error\", error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * login is a function that redirects the user to the Gneiss authentication service for authentication.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n */\r\n public login(req: Request, res: Response): void {\r\n try {\r\n if (!this.loginUrl) {\r\n throw new Error('Login URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n res.redirect(this.loginUrl + `?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}`);\r\n } catch (error) {\r\n console.error('Error in login middleware:', error);\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n\r\n /**\r\n * logout is a function that redirects the user to the Gneiss logout service.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n */\r\n public logout(req: Request, res: Response): void {\r\n const cookies = parseCookies(req);\r\n try {\r\n if (!this.logoutUrl) {\r\n throw new Error('Logout URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n if (!this.invalidateUrl) {\r\n throw new Error('Invalidate URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n if (cookies?.accessToken) { // Only invalidate the access token if it exists\r\n axios.post(this.invalidateUrl, {}, { // Invalidate the access token\r\n headers: {\r\n \"Authorization\": `Bearer ${cookies?.accessToken}`\r\n }\r\n });\r\n }\r\n clearCookies(res); // clear the access and refresh cookies\r\n res.redirect(this.logoutUrl + `?redirect_url=${this.config.baseUrl}`); // logout from Gneiss by visiting SSO logout page\r\n } catch (error) {\r\n console.error('Error in logout middleware:', error);\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n}\r\n\r\nexport default ExpressAuthGneissClient;\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_axios", "dotenv", "axios", "import_axios", "axios"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// src/core/AuthGneissCore.ts
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import { AxiosError } from "axios";
|
|
5
|
+
dotenv.config();
|
|
6
|
+
var AuthGneissCore = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.gneissEnpoint = process.env.GNEISS_ENDPOINT;
|
|
10
|
+
this.loginUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/login` : void 0;
|
|
11
|
+
this.logoutUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/logout` : void 0;
|
|
12
|
+
this.invalidateUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/invalidate_token` : void 0;
|
|
13
|
+
let errorMsgs = [];
|
|
14
|
+
if (!process.env.GNEISS_ENDPOINT) {
|
|
15
|
+
errorMsgs.push("GNEISS_ENDPOINT is not set in environment variables");
|
|
16
|
+
}
|
|
17
|
+
if (!process.env.NODE_ENV) {
|
|
18
|
+
errorMsgs.push("NODE_ENV is not set in environment variables");
|
|
19
|
+
}
|
|
20
|
+
if (errorMsgs.length > 0) {
|
|
21
|
+
throw new Error(errorMsgs.join("\n"));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* getTokens is a method that exchanges an authentication code for access and refresh tokens.
|
|
26
|
+
* The client id and secret are passed as basic auth headers to authenticate the client itself.
|
|
27
|
+
* @param authCode - The authentication code received from the Gneiss authentication service.
|
|
28
|
+
* @returns A promise that resolves to an object containing the access and refresh tokens.
|
|
29
|
+
*/
|
|
30
|
+
async getTokens(authCode) {
|
|
31
|
+
try {
|
|
32
|
+
const url = `${this.gneissEnpoint}/auth/access_token?auth_code=${authCode}`;
|
|
33
|
+
const encodedClientId = btoa(this.config.clientId);
|
|
34
|
+
const encodedClientSecret = btoa(this.config.clientSecret);
|
|
35
|
+
const response = await axios.post(url, {}, {
|
|
36
|
+
headers: {
|
|
37
|
+
"Authorization": `Basic ${encodedClientId}:${encodedClientSecret}`
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
accessToken: response.data.access_token,
|
|
42
|
+
refreshToken: response.data.refresh_token,
|
|
43
|
+
tokenType: response.data.token_type
|
|
44
|
+
};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* refreshToken is a method that refreshes the access token using the refresh token.
|
|
51
|
+
* @param refreshToken - The refresh token to be used for token refresh.
|
|
52
|
+
* @returns A promise that resolves to the refreshed access token.
|
|
53
|
+
*/
|
|
54
|
+
async refreshToken(refreshToken) {
|
|
55
|
+
try {
|
|
56
|
+
const url = `${this.gneissEnpoint}/auth/refresh`;
|
|
57
|
+
const response = await axios.post(url, {
|
|
58
|
+
headers: {
|
|
59
|
+
"Authorization": `Bearer ${refreshToken}`
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return response.data.accessToken;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* getUserData is a method that fetches user data using the access token.
|
|
69
|
+
* @param accessToken - The access token to be used for user data fetching.
|
|
70
|
+
* @returns A promise that resolves to the user data.
|
|
71
|
+
*/
|
|
72
|
+
async getUserData(accessToken) {
|
|
73
|
+
const url = `${this.gneissEnpoint}/resource/user_data`;
|
|
74
|
+
const response = await axios.get(url, {
|
|
75
|
+
headers: {
|
|
76
|
+
"Authorization": `Bearer ${accessToken}`
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (response.status === 200) {
|
|
80
|
+
return response.data;
|
|
81
|
+
}
|
|
82
|
+
throw new Error("Failed to fetch user data");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* validateToken is a method that validates the access token.
|
|
86
|
+
* @param token - The access token to be validated.
|
|
87
|
+
* @returns A promise that resolves to a boolean indicating the validity of the token.
|
|
88
|
+
*/
|
|
89
|
+
async validateToken(token) {
|
|
90
|
+
try {
|
|
91
|
+
console.log("DEBUG: token", token);
|
|
92
|
+
if (!token) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const url = `${this.gneissEnpoint}/auth/validate_token`;
|
|
96
|
+
const response = await axios.get(url, {
|
|
97
|
+
headers: {
|
|
98
|
+
"Authorization": `Bearer ${token}`
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return response.status === 200;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof AxiosError && error.response?.status === 401) {
|
|
104
|
+
return false;
|
|
105
|
+
} else {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* getLoginUrl is a method that returns the login URL.
|
|
112
|
+
* @returns The login URL.
|
|
113
|
+
*/
|
|
114
|
+
getLoginUrl() {
|
|
115
|
+
return this.loginUrl;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var AuthGneissCore_default = AuthGneissCore;
|
|
119
|
+
|
|
120
|
+
// src/utils/storage/cookieHandling.ts
|
|
121
|
+
import { decode } from "jsonwebtoken";
|
|
122
|
+
function setAccessToken(res, accessToken) {
|
|
123
|
+
const decodedToken = decode(accessToken);
|
|
124
|
+
if (!decodedToken.exp) {
|
|
125
|
+
throw new Error("Access token does not contain an expiration time");
|
|
126
|
+
}
|
|
127
|
+
res.cookie("accessToken", accessToken, {
|
|
128
|
+
httpOnly: true,
|
|
129
|
+
secure: process.env.NODE_ENV === "production",
|
|
130
|
+
sameSite: "strict",
|
|
131
|
+
maxAge: decodedToken.exp * 1e3 - Date.now()
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function setRefreshToken(res, refreshToken) {
|
|
135
|
+
const decodedToken = decode(refreshToken);
|
|
136
|
+
if (!decodedToken.exp) {
|
|
137
|
+
throw new Error("Refresh token does not contain an expiration time");
|
|
138
|
+
}
|
|
139
|
+
res.cookie("refreshToken", refreshToken, {
|
|
140
|
+
httpOnly: true,
|
|
141
|
+
secure: process.env.NODE_ENV === "production",
|
|
142
|
+
sameSite: "strict",
|
|
143
|
+
maxAge: decodedToken.exp * 1e3 - Date.now()
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function parseCookies(req) {
|
|
147
|
+
const cookies = req.headers.cookie;
|
|
148
|
+
if (!cookies) {
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
return cookies.split(";").reduce((acc, cookie) => {
|
|
152
|
+
const [key, value] = cookie.split("=").map((s) => s.trim());
|
|
153
|
+
acc[key] = value;
|
|
154
|
+
return acc;
|
|
155
|
+
}, {});
|
|
156
|
+
}
|
|
157
|
+
function clearCookies(res) {
|
|
158
|
+
res.clearCookie("accessToken", {
|
|
159
|
+
httpOnly: true,
|
|
160
|
+
secure: process.env.NODE_ENV === "production",
|
|
161
|
+
sameSite: "strict",
|
|
162
|
+
path: "/"
|
|
163
|
+
});
|
|
164
|
+
res.clearCookie("refreshToken", {
|
|
165
|
+
httpOnly: true,
|
|
166
|
+
secure: process.env.NODE_ENV === "production",
|
|
167
|
+
sameSite: "strict",
|
|
168
|
+
path: "/"
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/frameworks/express/middleware/ExpressAuthGneissClient.ts
|
|
173
|
+
import { AxiosError as AxiosError2 } from "axios";
|
|
174
|
+
import axios2 from "axios";
|
|
175
|
+
var ExpressAuthGneissClient = class extends AuthGneissCore_default {
|
|
176
|
+
constructor(config) {
|
|
177
|
+
super(config);
|
|
178
|
+
this.requireAuth = this.requireAuth.bind(this);
|
|
179
|
+
this.handleCallBack = this.handleCallBack.bind(this);
|
|
180
|
+
this.login = this.login.bind(this);
|
|
181
|
+
this.logout = this.logout.bind(this);
|
|
182
|
+
this.getUser = this.getUser.bind(this);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* requireAuth is a middleware function that checks if the access token is valid.
|
|
186
|
+
* If the access token is not valid, it attempts to refresh the token using the refresh token.
|
|
187
|
+
* If the refresh token is not valid, it redirects the user to the login page.
|
|
188
|
+
* @param req - The request object.
|
|
189
|
+
* @param res - The response object.
|
|
190
|
+
* @param next - The next middleware function.
|
|
191
|
+
*/
|
|
192
|
+
async requireAuth(req, res, next) {
|
|
193
|
+
const cookies = parseCookies(req);
|
|
194
|
+
console.log("DEBUG: cookies", cookies);
|
|
195
|
+
try {
|
|
196
|
+
const isAccessTokenValid = await this.validateToken(cookies?.accessToken);
|
|
197
|
+
if (!isAccessTokenValid) {
|
|
198
|
+
const newAccessToken = await this.refreshToken(cookies?.refreshToken);
|
|
199
|
+
if (newAccessToken) {
|
|
200
|
+
setAccessToken(res, newAccessToken);
|
|
201
|
+
} else {
|
|
202
|
+
const returnToUrl = req.originalUrl;
|
|
203
|
+
res.redirect(`${this.loginUrl}?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}&return_to_url=${returnToUrl}`);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
next();
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error instanceof AxiosError2) {
|
|
210
|
+
res.status(error.response?.status || 500).send(error.response?.data || "Internal server error");
|
|
211
|
+
} else {
|
|
212
|
+
res.status(500).send("Internal server error");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* getUserData is a middleware function that fetches user data using the access token.
|
|
218
|
+
* @param req - The request object.
|
|
219
|
+
* @param res - The response object.
|
|
220
|
+
* @param next - The next middleware function.
|
|
221
|
+
*/
|
|
222
|
+
async getUser(req, res) {
|
|
223
|
+
const cookies = parseCookies(req);
|
|
224
|
+
const accessToken = cookies?.accessToken;
|
|
225
|
+
if (!accessToken) {
|
|
226
|
+
throw new Error("No access token found in request cookies");
|
|
227
|
+
}
|
|
228
|
+
const userData = await this.getUserData(accessToken);
|
|
229
|
+
res.status(200).send(userData);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* handleCallBack is a middleware function that handles the callback from the authentication service.
|
|
233
|
+
* It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.
|
|
234
|
+
* @param req - The request object.
|
|
235
|
+
* @param res - The response object.
|
|
236
|
+
* @param next - The next middleware function.
|
|
237
|
+
*/
|
|
238
|
+
async handleCallBack(req, res, next) {
|
|
239
|
+
try {
|
|
240
|
+
const authCode = req.query.auth_code;
|
|
241
|
+
const returnToUrl = req.query.return_to_url;
|
|
242
|
+
if (!authCode) {
|
|
243
|
+
throw new Error("No auth code found in request url parameters");
|
|
244
|
+
}
|
|
245
|
+
const tokens = await this.getTokens(authCode);
|
|
246
|
+
setAccessToken(res, tokens.accessToken);
|
|
247
|
+
setRefreshToken(res, tokens.refreshToken);
|
|
248
|
+
if (returnToUrl) {
|
|
249
|
+
res.redirect(returnToUrl);
|
|
250
|
+
} else {
|
|
251
|
+
res.redirect("/");
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (error instanceof AxiosError2) {
|
|
255
|
+
res.status(error.response?.status || 500).send(error.response?.data || "Internal server error");
|
|
256
|
+
console.error("DEBUG: error", error);
|
|
257
|
+
} else {
|
|
258
|
+
res.status(500).send("Internal server error");
|
|
259
|
+
console.error("DEBUG: error", error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* login is a function that redirects the user to the Gneiss authentication service for authentication.
|
|
265
|
+
* @param req - The request object.
|
|
266
|
+
* @param res - The response object.
|
|
267
|
+
*/
|
|
268
|
+
login(req, res) {
|
|
269
|
+
try {
|
|
270
|
+
if (!this.loginUrl) {
|
|
271
|
+
throw new Error("Login URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
272
|
+
}
|
|
273
|
+
res.redirect(this.loginUrl + `?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}`);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error("Error in login middleware:", error);
|
|
276
|
+
res.status(500).send("Internal server error");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* logout is a function that redirects the user to the Gneiss logout service.
|
|
281
|
+
* @param req - The request object.
|
|
282
|
+
* @param res - The response object.
|
|
283
|
+
*/
|
|
284
|
+
logout(req, res) {
|
|
285
|
+
const cookies = parseCookies(req);
|
|
286
|
+
try {
|
|
287
|
+
if (!this.logoutUrl) {
|
|
288
|
+
throw new Error("Logout URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
289
|
+
}
|
|
290
|
+
if (!this.invalidateUrl) {
|
|
291
|
+
throw new Error("Invalidate URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.");
|
|
292
|
+
}
|
|
293
|
+
if (cookies?.accessToken) {
|
|
294
|
+
axios2.post(this.invalidateUrl, {}, {
|
|
295
|
+
// Invalidate the access token
|
|
296
|
+
headers: {
|
|
297
|
+
"Authorization": `Bearer ${cookies?.accessToken}`
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
clearCookies(res);
|
|
302
|
+
res.redirect(this.logoutUrl + `?redirect_url=${this.config.baseUrl}`);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error("Error in logout middleware:", error);
|
|
305
|
+
res.status(500).send("Internal server error");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var ExpressAuthGneissClient_default = ExpressAuthGneissClient;
|
|
310
|
+
export {
|
|
311
|
+
AuthGneissCore_default as AuthGneissCore,
|
|
312
|
+
ExpressAuthGneissClient_default as ExpressAuthGneissClient
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/AuthGneissCore.ts", "../../src/utils/storage/cookieHandling.ts", "../../src/frameworks/express/middleware/ExpressAuthGneissClient.ts"],
|
|
4
|
+
"sourcesContent": ["import { AuthGneissCoreConfig } from \"@core/types\";\r\nimport axios, { AxiosResponse } from \"axios\";\r\nimport { Tokens } from \"@core/types\";\r\nimport { JwtPayload } from \"jsonwebtoken\";\r\nimport dotenv from \"dotenv\";\r\nimport { AxiosError } from \"axios\";\r\n\r\n//load environment variables\r\ndotenv.config();\r\n\r\n/**\r\n * AuthGneissCore provides core functionality for OAuth2 authentication flow with Gneiss authentication service.\r\n * It handles token exchange, token refresh, user data fetching, and token validation.\r\n * \r\n * This class serves as a base class that can be extended by framework-specific implementations\r\n * to provide authentication middleware and handlers.\r\n */\r\nclass AuthGneissCore {\r\n protected config: AuthGneissCoreConfig; // Configuration object\r\n protected gneissEnpoint : string | undefined\r\n protected loginUrl : string | undefined\r\n protected logoutUrl : string | undefined\r\n protected invalidateUrl : string | undefined\r\n\r\n constructor(\r\n config: AuthGneissCoreConfig\r\n ) {\r\n this.config = config;\r\n this.gneissEnpoint = process.env.GNEISS_ENDPOINT // Gneiss endpoint\r\n this.loginUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/login` : undefined; // Login URL\r\n this.logoutUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/logout` : undefined; // Logout URL\r\n this.invalidateUrl = this.gneissEnpoint ? `${this.gneissEnpoint}/auth/invalidate_token` : undefined; // Invalidate URL\r\n\r\n //check if environment variables are set\r\n let errorMsgs = [];\r\n if (!process.env.GNEISS_ENDPOINT) {\r\n errorMsgs.push(\"GNEISS_ENDPOINT is not set in environment variables\");\r\n }\r\n if (!process.env.NODE_ENV) {\r\n errorMsgs.push(\"NODE_ENV is not set in environment variables\");\r\n }\r\n if (errorMsgs.length > 0) {\r\n throw new Error(errorMsgs.join(\"\\n\"));\r\n }\r\n }\r\n\r\n /**\r\n * getTokens is a method that exchanges an authentication code for access and refresh tokens.\r\n * The client id and secret are passed as basic auth headers to authenticate the client itself.\r\n * @param authCode - The authentication code received from the Gneiss authentication service.\r\n * @returns A promise that resolves to an object containing the access and refresh tokens.\r\n */\r\n async getTokens(authCode : string) : Promise<Tokens> {\r\n try {\r\n const url : string = `${this.gneissEnpoint}/auth/access_token?auth_code=${authCode}`;\r\n //Encode in base64 before transport\r\n const encodedClientId = btoa(this.config.clientId);\r\n const encodedClientSecret = btoa(this.config.clientSecret);\r\n const response : AxiosResponse = await axios.post(url, {}, {\r\n headers: {\r\n \"Authorization\": `Basic ${encodedClientId}:${encodedClientSecret}`\r\n }\r\n });\r\n return {\r\n accessToken: response.data.access_token,\r\n refreshToken: response.data.refresh_token,\r\n tokenType: response.data.token_type\r\n } as Tokens;\r\n } catch (error) {\r\n // console.error(\"Error in getTokens:\", error);\r\n throw error;\r\n }\r\n }\r\n \r\n /**\r\n * refreshToken is a method that refreshes the access token using the refresh token.\r\n * @param refreshToken - The refresh token to be used for token refresh.\r\n * @returns A promise that resolves to the refreshed access token.\r\n */\r\n async refreshToken(refreshToken: string): Promise<string | null> {\r\n try {\r\n const url : string = `${this.gneissEnpoint}/auth/refresh`;\r\n const response : AxiosResponse = await axios.post(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${refreshToken}`\r\n }\r\n });\r\n return response.data.accessToken as string;\r\n } catch (error) {\r\n // console.error(\"Error in refreshToken:\", error);\r\n return null;\r\n }\r\n }\r\n \r\n /**\r\n * getUserData is a method that fetches user data using the access token.\r\n * @param accessToken - The access token to be used for user data fetching.\r\n * @returns A promise that resolves to the user data.\r\n */\r\n async getUserData(accessToken: string) {\r\n const url : string = `${this.gneissEnpoint}/resource/user_data`;\r\n const response : AxiosResponse = await axios.get(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${accessToken}`\r\n }\r\n });\r\n if (response.status === 200) {\r\n return response.data;\r\n }\r\n throw new Error(\"Failed to fetch user data\");\r\n }\r\n \r\n /**\r\n * validateToken is a method that validates the access token.\r\n * @param token - The access token to be validated.\r\n * @returns A promise that resolves to a boolean indicating the validity of the token.\r\n */\r\n async validateToken(token: string): Promise<boolean> {\r\n try {\r\n console.log(\"DEBUG: token\", token);\r\n // Token validation logic\r\n if (!token) {\r\n return false;\r\n }\r\n const url : string = `${this.gneissEnpoint}/auth/validate_token`;\r\n const response : AxiosResponse = await axios.get(url, {\r\n headers: {\r\n \"Authorization\": `Bearer ${token}`\r\n }\r\n });\r\n return response.status === 200;\r\n } catch (error) {\r\n // console.error(\"Error in validateToken:\", error);\r\n if (error instanceof AxiosError && error.response?.status === 401) {\r\n return false;\r\n } else {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * getLoginUrl is a method that returns the login URL.\r\n * @returns The login URL.\r\n */\r\n public getLoginUrl() : string | undefined {\r\n return this.loginUrl;\r\n }\r\n}\r\n\r\nexport default AuthGneissCore;\r\n", "import { Response } from \"express\";\r\nimport { JwtPayload, decode } from \"jsonwebtoken\";\r\nimport { Request } from \"express\";\r\n\r\n/**\r\n * Set the access token in the response cookies.\r\n * @param res - The response object.\r\n * @param accessToken - The access token to set.\r\n */\r\nfunction setAccessToken(res: Response, accessToken: string) {\r\n\r\n const decodedToken = decode(accessToken) as JwtPayload;\r\n \r\n // decoded.exp is in seconds since epoch\r\n // Date.now() returns milliseconds since epoch\r\n // maxAge needs milliseconds remaining\r\n if (!decodedToken.exp) {\r\n throw new Error(\"Access token does not contain an expiration time\");\r\n }\r\n \r\n res.cookie('accessToken', accessToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n maxAge: (decodedToken.exp * 1000) - Date.now()\r\n });\r\n}\r\n\r\n/**\r\n * Set the refresh token in the response cookies.\r\n * @param res - The response object.\r\n * @param refreshToken - The refresh token to set.\r\n */\r\nfunction setRefreshToken(res: Response, refreshToken: string) {\r\n\r\n const decodedToken = decode(refreshToken) as JwtPayload;\r\n\r\n if (!decodedToken.exp) {\r\n throw new Error(\"Refresh token does not contain an expiration time\");\r\n }\r\n\r\n res.cookie('refreshToken', refreshToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n maxAge: (decodedToken.exp * 1000) - Date.now()\r\n });\r\n}\r\n\r\nfunction parseCookies(req: Request) : { [key: string]: string } {\r\n const cookies = req.headers.cookie;\r\n if (!cookies) {\r\n return {};\r\n }\r\n return cookies.split(';').reduce((acc: { [key: string]: string }, cookie) => {\r\n const [key, value] = cookie.split('=').map(s => s.trim());\r\n acc[key] = value;\r\n return acc;\r\n }, {});\r\n}\r\n\r\nfunction clearCookies(res: Response) {\r\n res.clearCookie(\"accessToken\", {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/'\r\n });\r\n res.clearCookie(\"refreshToken\", {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/'\r\n });\r\n}\r\n\r\nexport { setAccessToken, setRefreshToken, parseCookies, clearCookies };\r\n", "import { AuthGneissCore, AuthGneissCoreConfig } from \"@core\";\r\nimport { Request, Response, NextFunction } from \"express\";\r\nimport { RequestWithTokens, Tokens } from \"@core/types\";\r\nimport { setAccessToken, setRefreshToken, parseCookies } from \"@utils\";\r\nimport { JwtPayload } from \"jsonwebtoken\";\r\nimport { AxiosError } from \"axios\";\r\nimport axios from \"axios\";\r\nimport { clearCookies } from \"@/utils/storage/cookieHandling\";\r\n\r\n/**\r\n * ExpressAuthGneissClient extends AuthGneissCore to provide Express-specific authentication middleware\r\n * and functionality for handling OAuth2 authentication flow with Gneiss authentication service.\r\n * \r\n * @extends AuthGneissCore\r\n * @example\r\n * const authClient = new ExpressAuthGneissClient({\r\n * clientId: 'your-client-id',\r\n * clientSecret: 'your-client-secret',\r\n * redirectUrl: 'your-redirect-url'\r\n * });\r\n */\r\nclass ExpressAuthGneissClient extends AuthGneissCore {\r\n\r\n constructor(\r\n config: AuthGneissCoreConfig\r\n ) {\r\n super(config);\r\n \r\n // Bind the methods in constructor\r\n this.requireAuth = this.requireAuth.bind(this);\r\n this.handleCallBack = this.handleCallBack.bind(this);\r\n this.login = this.login.bind(this);\r\n this.logout = this.logout.bind(this);\r\n this.getUser = this.getUser.bind(this);\r\n }\r\n\r\n /**\r\n * requireAuth is a middleware function that checks if the access token is valid.\r\n * If the access token is not valid, it attempts to refresh the token using the refresh token.\r\n * If the refresh token is not valid, it redirects the user to the login page.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async requireAuth(req: Request, res: Response, next: NextFunction): Promise<void> {\r\n const cookies = parseCookies(req);\r\n //Check for the existence of the access token\r\n console.log(\"DEBUG: cookies\", cookies);\r\n try {\r\n const isAccessTokenValid : boolean = await this.validateToken(cookies?.accessToken);\r\n if (!isAccessTokenValid) { //if the access token is not valid\r\n //try to refresh the token\r\n const newAccessToken : string | null = await this.refreshToken(cookies?.refreshToken);\r\n if (newAccessToken) { //\r\n setAccessToken(res, newAccessToken);\r\n }\r\n else {\r\n // no access token or valid refresh token, redirect to login\r\n const returnToUrl : string | undefined = req.originalUrl as string;\r\n res.redirect(`${this.loginUrl}?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}&return_to_url=${returnToUrl}`);\r\n }\r\n }\r\n else {\r\n // access token is valid, continue to the next middleware or route handler\r\n next();\r\n }\r\n } catch (error) {\r\n // console.error('Error in requireAuth middleware:', error);\r\n if (error instanceof AxiosError) {\r\n res.status((error as AxiosError).response?.status || 500).send((error as AxiosError).response?.data || 'Internal server error');\r\n } else {\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * getUserData is a middleware function that fetches user data using the access token.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async getUser(req: Request, res: Response): Promise<void> {\r\n const cookies = parseCookies(req);\r\n const accessToken = cookies?.accessToken;\r\n if (!accessToken) {\r\n throw new Error(\"No access token found in request cookies\");\r\n }\r\n const userData = await this.getUserData(accessToken);\r\n res.status(200).send(userData);\r\n }\r\n\r\n /**\r\n * handleCallBack is a middleware function that handles the callback from the authentication service.\r\n * It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n * @param next - The next middleware function.\r\n */\r\n public async handleCallBack(\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n ): Promise<void> {\r\n try {\r\n const authCode: string | undefined = req.query.auth_code as string\r\n const returnToUrl : string | undefined = req.query.return_to_url as string;\r\n if (!authCode) {\r\n throw new Error(\"No auth code found in request url parameters\");\r\n }\r\n\r\n const tokens: Tokens = await this.getTokens(authCode);\r\n \r\n // Set the access and refresh tokens in the response cookies\r\n setAccessToken(res, tokens.accessToken);\r\n setRefreshToken(res, tokens.refreshToken);\r\n\r\n if (returnToUrl) {\r\n // Go to the original request url\r\n res.redirect(returnToUrl);\r\n }\r\n else {\r\n // Go to the root url\r\n res.redirect(\"/\")\r\n }\r\n } catch (error) {\r\n // console.error('Error in handleCallBack middleware:', error);\r\n if (error instanceof AxiosError) {\r\n res.status((error as AxiosError).response?.status || 500).send((error as AxiosError).response?.data || 'Internal server error');\r\n console.error(\"DEBUG: error\", error);\r\n } else {\r\n res.status(500).send('Internal server error');\r\n console.error(\"DEBUG: error\", error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * login is a function that redirects the user to the Gneiss authentication service for authentication.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n */\r\n public login(req: Request, res: Response): void {\r\n try {\r\n if (!this.loginUrl) {\r\n throw new Error('Login URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n res.redirect(this.loginUrl + `?redirect_url=${this.config.baseUrl}${this.config.redirectUrl}`);\r\n } catch (error) {\r\n console.error('Error in login middleware:', error);\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n\r\n /**\r\n * logout is a function that redirects the user to the Gneiss logout service.\r\n * @param req - The request object.\r\n * @param res - The response object.\r\n */\r\n public logout(req: Request, res: Response): void {\r\n const cookies = parseCookies(req);\r\n try {\r\n if (!this.logoutUrl) {\r\n throw new Error('Logout URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n if (!this.invalidateUrl) {\r\n throw new Error('Invalidate URL is not configured. Check if GNEISS_ENDPOINT environment variable is set.');\r\n }\r\n if (cookies?.accessToken) { // Only invalidate the access token if it exists\r\n axios.post(this.invalidateUrl, {}, { // Invalidate the access token\r\n headers: {\r\n \"Authorization\": `Bearer ${cookies?.accessToken}`\r\n }\r\n });\r\n }\r\n clearCookies(res); // clear the access and refresh cookies\r\n res.redirect(this.logoutUrl + `?redirect_url=${this.config.baseUrl}`); // logout from Gneiss by visiting SSO logout page\r\n } catch (error) {\r\n console.error('Error in logout middleware:', error);\r\n res.status(500).send('Internal server error');\r\n }\r\n }\r\n}\r\n\r\nexport default ExpressAuthGneissClient;\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["AxiosError", "axios", "AxiosError", "axios"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AuthGneissCoreConfig } from "@core/types";
|
|
2
|
+
import { Tokens } from "@core/types";
|
|
3
|
+
/**
|
|
4
|
+
* AuthGneissCore provides core functionality for OAuth2 authentication flow with Gneiss authentication service.
|
|
5
|
+
* It handles token exchange, token refresh, user data fetching, and token validation.
|
|
6
|
+
*
|
|
7
|
+
* This class serves as a base class that can be extended by framework-specific implementations
|
|
8
|
+
* to provide authentication middleware and handlers.
|
|
9
|
+
*/
|
|
10
|
+
declare class AuthGneissCore {
|
|
11
|
+
protected config: AuthGneissCoreConfig;
|
|
12
|
+
protected gneissEnpoint: string | undefined;
|
|
13
|
+
protected loginUrl: string | undefined;
|
|
14
|
+
protected logoutUrl: string | undefined;
|
|
15
|
+
protected invalidateUrl: string | undefined;
|
|
16
|
+
constructor(config: AuthGneissCoreConfig);
|
|
17
|
+
/**
|
|
18
|
+
* getTokens is a method that exchanges an authentication code for access and refresh tokens.
|
|
19
|
+
* The client id and secret are passed as basic auth headers to authenticate the client itself.
|
|
20
|
+
* @param authCode - The authentication code received from the Gneiss authentication service.
|
|
21
|
+
* @returns A promise that resolves to an object containing the access and refresh tokens.
|
|
22
|
+
*/
|
|
23
|
+
getTokens(authCode: string): Promise<Tokens>;
|
|
24
|
+
/**
|
|
25
|
+
* refreshToken is a method that refreshes the access token using the refresh token.
|
|
26
|
+
* @param refreshToken - The refresh token to be used for token refresh.
|
|
27
|
+
* @returns A promise that resolves to the refreshed access token.
|
|
28
|
+
*/
|
|
29
|
+
refreshToken(refreshToken: string): Promise<string | null>;
|
|
30
|
+
/**
|
|
31
|
+
* getUserData is a method that fetches user data using the access token.
|
|
32
|
+
* @param accessToken - The access token to be used for user data fetching.
|
|
33
|
+
* @returns A promise that resolves to the user data.
|
|
34
|
+
*/
|
|
35
|
+
getUserData(accessToken: string): Promise<any>;
|
|
36
|
+
/**
|
|
37
|
+
* validateToken is a method that validates the access token.
|
|
38
|
+
* @param token - The access token to be validated.
|
|
39
|
+
* @returns A promise that resolves to a boolean indicating the validity of the token.
|
|
40
|
+
*/
|
|
41
|
+
validateToken(token: string): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* getLoginUrl is a method that returns the login URL.
|
|
44
|
+
* @returns The login URL.
|
|
45
|
+
*/
|
|
46
|
+
getLoginUrl(): string | undefined;
|
|
47
|
+
}
|
|
48
|
+
export default AuthGneissCore;
|
|
49
|
+
//# sourceMappingURL=AuthGneissCore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthGneissCore.d.ts","sourceRoot":"","sources":["../../../../src/core/AuthGneissCore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;;GAMG;AACH,cAAM,cAAc;IAChB,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACvC,SAAS,CAAC,aAAa,EAAG,MAAM,GAAG,SAAS,CAAA;IAC5C,SAAS,CAAC,QAAQ,EAAG,MAAM,GAAG,SAAS,CAAA;IACvC,SAAS,CAAC,SAAS,EAAG,MAAM,GAAG,SAAS,CAAA;IACxC,SAAS,CAAC,aAAa,EAAG,MAAM,GAAG,SAAS,CAAA;gBAGxC,MAAM,EAAE,oBAAoB;IAqBhC;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAG,MAAM,GAAI,OAAO,CAAC,MAAM,CAAC;IAsBpD;;;;OAIG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAehE;;;;OAIG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM;IAarC;;;;OAIG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBpD;;;OAGG;IACI,WAAW,IAAK,MAAM,GAAG,SAAS;CAG5C;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAC,CAAC;AACzB,YAAY,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Request } from "express";
|
|
2
|
+
interface AuthGneissCoreConfig {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
redirectUrl: string;
|
|
7
|
+
}
|
|
8
|
+
interface Tokens {
|
|
9
|
+
accessToken: string;
|
|
10
|
+
refreshToken: string;
|
|
11
|
+
tokenType: string;
|
|
12
|
+
}
|
|
13
|
+
interface RequestWithTokens extends Request {
|
|
14
|
+
cookies: {
|
|
15
|
+
accessToken: string;
|
|
16
|
+
refreshToken: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export { AuthGneissCoreConfig, Tokens, RequestWithTokens };
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,UAAU,oBAAoB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,MAAM;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,iBAAkB,SAAQ,OAAO;IACvC,OAAO,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAA;CACJ;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AuthGneissCore, AuthGneissCoreConfig } from "@core";
|
|
2
|
+
import { Request, Response, NextFunction } from "express";
|
|
3
|
+
/**
|
|
4
|
+
* ExpressAuthGneissClient extends AuthGneissCore to provide Express-specific authentication middleware
|
|
5
|
+
* and functionality for handling OAuth2 authentication flow with Gneiss authentication service.
|
|
6
|
+
*
|
|
7
|
+
* @extends AuthGneissCore
|
|
8
|
+
* @example
|
|
9
|
+
* const authClient = new ExpressAuthGneissClient({
|
|
10
|
+
* clientId: 'your-client-id',
|
|
11
|
+
* clientSecret: 'your-client-secret',
|
|
12
|
+
* redirectUrl: 'your-redirect-url'
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
declare class ExpressAuthGneissClient extends AuthGneissCore {
|
|
16
|
+
constructor(config: AuthGneissCoreConfig);
|
|
17
|
+
/**
|
|
18
|
+
* requireAuth is a middleware function that checks if the access token is valid.
|
|
19
|
+
* If the access token is not valid, it attempts to refresh the token using the refresh token.
|
|
20
|
+
* If the refresh token is not valid, it redirects the user to the login page.
|
|
21
|
+
* @param req - The request object.
|
|
22
|
+
* @param res - The response object.
|
|
23
|
+
* @param next - The next middleware function.
|
|
24
|
+
*/
|
|
25
|
+
requireAuth(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* getUserData is a middleware function that fetches user data using the access token.
|
|
28
|
+
* @param req - The request object.
|
|
29
|
+
* @param res - The response object.
|
|
30
|
+
* @param next - The next middleware function.
|
|
31
|
+
*/
|
|
32
|
+
getUser(req: Request, res: Response): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* handleCallBack is a middleware function that handles the callback from the authentication service.
|
|
35
|
+
* It extracts the auth code from the request URL parameters, exchanges it for tokens, and sets the access and refresh tokens in the response cookies.
|
|
36
|
+
* @param req - The request object.
|
|
37
|
+
* @param res - The response object.
|
|
38
|
+
* @param next - The next middleware function.
|
|
39
|
+
*/
|
|
40
|
+
handleCallBack(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* login is a function that redirects the user to the Gneiss authentication service for authentication.
|
|
43
|
+
* @param req - The request object.
|
|
44
|
+
* @param res - The response object.
|
|
45
|
+
*/
|
|
46
|
+
login(req: Request, res: Response): void;
|
|
47
|
+
/**
|
|
48
|
+
* logout is a function that redirects the user to the Gneiss logout service.
|
|
49
|
+
* @param req - The request object.
|
|
50
|
+
* @param res - The response object.
|
|
51
|
+
*/
|
|
52
|
+
logout(req: Request, res: Response): void;
|
|
53
|
+
}
|
|
54
|
+
export default ExpressAuthGneissClient;
|
|
55
|
+
//# sourceMappingURL=ExpressAuthGneissClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressAuthGneissClient.d.ts","sourceRoot":"","sources":["../../../../../../src/frameworks/express/middleware/ExpressAuthGneissClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ1D;;;;;;;;;;;GAWG;AACH,cAAM,uBAAwB,SAAQ,cAAc;gBAG5C,MAAM,EAAE,oBAAoB;IAYhC;;;;;;;OAOG;IACU,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCxF;;;;;OAKG;IACU,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhE;;;;;;OAMG;IACU,cAAc,CACvB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC;IAkChB;;;;OAIG;IACI,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI;IAY/C;;;;OAIG;IACI,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI;CAuBnD;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/frameworks/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,MAAM,8CAA8C,CAAC;AAEnF,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Response } from "express";
|
|
2
|
+
import { Request } from "express";
|
|
3
|
+
/**
|
|
4
|
+
* Set the access token in the response cookies.
|
|
5
|
+
* @param res - The response object.
|
|
6
|
+
* @param accessToken - The access token to set.
|
|
7
|
+
*/
|
|
8
|
+
declare function setAccessToken(res: Response, accessToken: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Set the refresh token in the response cookies.
|
|
11
|
+
* @param res - The response object.
|
|
12
|
+
* @param refreshToken - The refresh token to set.
|
|
13
|
+
*/
|
|
14
|
+
declare function setRefreshToken(res: Response, refreshToken: string): void;
|
|
15
|
+
declare function parseCookies(req: Request): {
|
|
16
|
+
[key: string]: string;
|
|
17
|
+
};
|
|
18
|
+
declare function clearCookies(res: Response): void;
|
|
19
|
+
export { setAccessToken, setRefreshToken, parseCookies, clearCookies };
|
|
20
|
+
//# sourceMappingURL=cookieHandling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookieHandling.d.ts","sourceRoot":"","sources":["../../../../../src/utils/storage/cookieHandling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC;;;;GAIG;AACH,iBAAS,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,QAiBzD;AAED;;;;GAIG;AACH,iBAAS,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,QAc3D;AAED,iBAAS,YAAY,CAAC,GAAG,EAAE,OAAO,GAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAU9D;AAED,iBAAS,YAAY,CAAC,GAAG,EAAE,QAAQ,QAalC;AAED,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressAuthGneissClient.integration.test.d.ts","sourceRoot":"","sources":["../../../../../../tests/frameworks/express/middleware/ExpressAuthGneissClient.integration.test.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gneiss/client-auth",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"main": "dist/cjs/index.js",
|
|
5
|
+
"module": "dist/esm/index.js",
|
|
6
|
+
"types": "dist/types/src/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/types/src/index.d.ts",
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/cjs/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "node build.js && tsc --emitDeclarationOnly --outDir dist/types",
|
|
17
|
+
"build:watch": "node build.js --watch",
|
|
18
|
+
"test": "jest",
|
|
19
|
+
"clean": "rd /s /q dist"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [],
|
|
22
|
+
"author": "Timothy Newton",
|
|
23
|
+
"license": "",
|
|
24
|
+
"description": "A client for the Gneiss authentication service",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/cookie-parser": "^1.4.8",
|
|
27
|
+
"@types/express": "^5.0.0",
|
|
28
|
+
"@types/jest": "^29.5.14",
|
|
29
|
+
"@types/node": "^22.9.0",
|
|
30
|
+
"@types/supertest": "^6.0.2",
|
|
31
|
+
"jest": "^29.7.0",
|
|
32
|
+
"ts-jest": "^29.2.5",
|
|
33
|
+
"typescript": "^5.6.3"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@types/jsonwebtoken": "^9.0.7",
|
|
37
|
+
"axios": "^1.7.7",
|
|
38
|
+
"cookie-parser": "^1.4.7",
|
|
39
|
+
"dotenv": "^16.4.5",
|
|
40
|
+
"esbuild": "^0.24.0",
|
|
41
|
+
"express": "^4.21.1",
|
|
42
|
+
"jsonwebtoken": "^9.0.2",
|
|
43
|
+
"nock": "^13.5.6",
|
|
44
|
+
"supertest": "^7.0.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"package.json"
|
|
50
|
+
]
|
|
51
|
+
}
|