Skip to content

Commit 85acf8a

Browse files
authored
Merge pull request #804 from bcgov/release/v1.28.0
Release/v1.28.0
2 parents d3cebc8 + 68197e7 commit 85acf8a

29 files changed

+12862
-23680
lines changed

backend/package-lock.json

Lines changed: 5544 additions & 6617 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@js-joda/locale_en": "^3.2.2",
2222
"async-retry": "^1.3.1",
2323
"atob": "2.1.2",
24-
"axios": "0.25.0",
24+
"axios": "0.28.0",
2525
"body-parser": "^1.19.0",
2626
"config": "^3.3.3",
2727
"connect-redis": "^5.1.0",
@@ -79,6 +79,11 @@
7979
"redis-mock": "^0.56.3",
8080
"supertest": "^6.1.3"
8181
},
82+
"overrides": {
83+
"semver": "7.7.1",
84+
"tough-cookie": "5.1.1",
85+
"xml2js": "0.6.2"
86+
},
8287
"eslintConfig": {
8388
"root": true,
8489
"env": {

backend/src/app.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const promMid = require("express-prometheus-middleware");
2727

2828
function addVersionToReq(req, res, next) {
2929
const { version } = req.params;
30-
console.log(version)
3130
// Check if the version is supported
3231
const supportedVersions = ["v1", "v2"];
3332
if (!supportedVersions.includes(version)) {
@@ -134,7 +133,8 @@ auth
134133
scope: discovery.scopes_supported,
135134
kc_idp_hint: config.get("server:idirIDPHint"),
136135
},
137-
(_issuer, _sub, profile, accessToken, refreshToken, done) => {
136+
(_issuer, _sub, profile, accessToken, refreshToken, params, done) => {
137+
const idToken = params.id_token;
138138
if (
139139
typeof accessToken === "undefined" ||
140140
accessToken === null ||
@@ -148,10 +148,33 @@ auth
148148
profile.jwtFrontend = auth.generateUiToken();
149149
profile.jwt = accessToken;
150150
profile.refreshToken = refreshToken;
151+
profile.idToken = idToken;
151152
return done(null, profile);
152153
}
153154
)
154155
);
156+
passport.use('oidcIDIRSilent', new OidcStrategy({
157+
issuer: discovery.issuer,
158+
authorizationURL: discovery.authorization_endpoint,
159+
tokenURL: discovery.token_endpoint,
160+
userInfoURL: discovery['userinfo_endpoint'],
161+
clientID: config.get('oidc:clientId'),
162+
clientSecret: config.get('oidc:clientSecret'),
163+
callbackURL: config.get('server:frontend') + '/api/auth/callback_idir_silent',
164+
scope: 'openid profile',
165+
kc_idp_hint: config.get('server:idirIDPHint')
166+
}, (_issuer, _sub, profile, accessToken, refreshToken, done) => {
167+
if ((typeof (accessToken) === 'undefined') || (accessToken === null) ||
168+
(typeof (refreshToken) === 'undefined') || (refreshToken === null)) {
169+
return done('No access token', null);
170+
}
171+
//Generate token for frontend validation
172+
//set access and refresh tokens
173+
profile.jwtFrontend = auth.generateUiToken();
174+
profile.jwt = accessToken;
175+
profile.refreshToken = refreshToken;
176+
return done(null, profile);
177+
}));
155178
//JWT strategy is used for authorization
156179
passport.use(
157180
"jwt",

backend/src/components/auth.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ const auth = {
199199
},
200200
}
201201
);
202-
203-
log.verbose("getServiceAccountToken Res", safeStringify(response.data));
204-
205202
let result = {};
206203
result.accessToken = response.data.access_token;
207204
return result.accessToken;

backend/src/components/utils.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@ let memCache = new cache.Cache();
1616

1717
axios.interceptors.request.use((axiosRequestConfig) => {
1818
axiosRequestConfig.headers["X-Client-Name"] = "GRAD-ADMIN";
19+
axiosRequestConfig.headers["Request-Source"] = "grad-admin";
1920
return axiosRequestConfig;
2021
});
2122

2223
async function getBackendServiceToken() {
2324
return await auth.getBackendServiceToken();
2425
}
2526

27+
function getUsernameFromToken(token) {
28+
try {
29+
const decoded = jsonwebtoken.decode(token); // Use decode if you don't need verification
30+
return decoded?.idir_username || null;
31+
32+
} catch (error) {
33+
console.error('Invalid token:', error);
34+
return null;
35+
}
36+
}
37+
2638
function getUser(req) {
2739
const thisSession = req.session;
2840
if (
@@ -69,10 +81,12 @@ function getAccessToken(req) {
6981

7082
async function deleteData(token, url, correlationID) {
7183
try {
84+
const username = getUsernameFromToken(token)
7285
const delConfig = {
7386
headers: {
7487
Authorization: `Bearer ${token}`,
7588
correlationID: correlationID || uuidv4(),
89+
"User-Name": username || 'N/A'
7690
},
7791
};
7892

@@ -165,11 +179,13 @@ async function getCommonServiceData(url, params) {
165179
}
166180

167181
async function getData(token, url, correlationID) {
182+
const username = getUsernameFromToken(token)
168183
try {
169184
const getDataConfig = {
170185
headers: {
171186
Authorization: `Bearer ${token}`,
172187
correlationID: correlationID || uuidv4(),
188+
"User-Name": username || 'N/A'
173189
},
174190
};
175191
// log.info('get Data Url', url);
@@ -190,9 +206,11 @@ async function getData(token, url, correlationID) {
190206

191207
async function getDataWithParams(token, url, params, correlationID) {
192208
try {
209+
const username = getUsernameFromToken(token)
193210
params.headers = {
194211
Authorization: `Bearer ${token}`,
195212
correlationID: correlationID || uuidv4(),
213+
"User-Name": username || 'N/A'
196214
};
197215

198216
log.info("get Data Url", url);
@@ -246,10 +264,12 @@ async function forwardPostReq(req, res, url) {
246264

247265
async function postData(token, url, data, correlationID) {
248266
try {
267+
const username = getUsernameFromToken(token)
249268
const postDataConfig = {
250269
headers: {
251270
Authorization: `Bearer ${token}`,
252271
correlationID: correlationID || uuidv4(),
272+
"User-Name": username || 'N/A'
253273
},
254274
maxContentLength: Infinity,
255275
maxBodyLength: Infinity,
@@ -289,10 +309,12 @@ async function postData(token, url, data, correlationID) {
289309

290310
async function putData(token, data, url, correlationID) {
291311
try {
312+
const username = getUsernameFromToken(token)
292313
const putDataConfig = {
293314
headers: {
294315
Authorization: `Bearer ${token}`,
295316
correlationID: correlationID || uuidv4(),
317+
"User-Name": username || 'N/A'
296318
},
297319
};
298320

backend/src/routes/auth.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ const roles = require("../components/roles");
88
const jsonwebtoken = require("jsonwebtoken");
99
const log = require("../components/logger");
1010
const HttpStatus = require("http-status-codes");
11+
const redis = require('../util/redis/redis-client');
1112
const { v4: uuidv4 } = require("uuid");
13+
const { v4: validate } = require("uuid");
1214
const { body, validationResult } = require("express-validator");
15+
const UnauthorizedRsp = {
16+
error: 'Unauthorized',
17+
error_description: 'Not logged in'
18+
};
1319

1420
const isValidStaffUserWithRoles = auth.isValidUserWithRoles(
1521
"GRAD_INFO_OFFICER",
@@ -228,4 +234,56 @@ router.get(
228234
}
229235
);
230236

237+
router.get('/silent_idir_login', async function (req, res, next) {
238+
const client = redis.getRedisClient();
239+
240+
if(!req.query.idir_guid){
241+
res.status(401).json(UnauthorizedRsp);
242+
}
243+
let idir_guid = req.query.idir_guid;
244+
console.log('idir_guid', idir_guid);
245+
if(req.query.studentDetails && req.query.studentID){
246+
let studentID = req.query.studentID;
247+
if (!validate(studentID)) {
248+
res.status(401).json('Invalid Student ID.');
249+
}
250+
251+
const redis_key_prefix = idir_guid.toLowerCase();
252+
253+
await client.set(redis_key_prefix + '::studentDetails', true, 'EX', 1800);
254+
await client.set(redis_key_prefix + '::studentID', studentID, 'EX', 1800);
255+
}
256+
else{
257+
res.status(401).json(UnauthorizedRsp);
258+
}
259+
260+
const authenticator = passport.authenticate('oidcIDIRSilent', { failureRedirect: 'error', scope: 'openid profile' });
261+
authenticator(req, res, next);
262+
});
263+
264+
router.get('/callback_idir_silent',
265+
passport.authenticate('oidcIDIRSilent', { failureRedirect: 'error', scope: 'openid profile'}),
266+
async (req, res) => {
267+
const idir_guid = req.session?.passport?.user?._json?.idir_guid;
268+
if(!idir_guid){
269+
await res.redirect(config.get('server:frontend') + '/unauthorized');
270+
return;
271+
}
272+
const client = redis.getRedisClient();
273+
const redis_key_prefix = idir_guid.toLowerCase();
274+
275+
let studentDetails = await client.get(redis_key_prefix + '::studentDetails');
276+
let studentID = await client.get(redis_key_prefix + '::studentID');
277+
278+
await client.del(redis_key_prefix + '::studentDetails');
279+
await client.del(redis_key_prefix + '::studentID');
280+
281+
if (studentDetails) {
282+
res.redirect(config.get('server:frontend') + '/student-profile/' + studentID);
283+
} else {
284+
res.status(401).json(UnauthorizedRsp);
285+
}
286+
}
287+
);
288+
231289
module.exports = router;

0 commit comments

Comments
 (0)