@ministryofjustice/hmpps-prisoner-auth 0.0.2 → 0.0.4

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 CHANGED
@@ -7,29 +7,39 @@
7
7
  The following steps will give an opinionated out of the box setup for applications where a launchpad user will be the main user in the system.
8
8
 
9
9
  1. Add the package `@ministryofjustice/hmpps-prisoner-auth` to your `package.json`
10
+ ```sh
11
+ npm add --save @ministryofjustice/hmpps-prisoner-auth
12
+ npm i
10
13
  ```
11
- $ npm add --save @ministryofjustice/hmpps-prisoner-auth
12
- $ npm i
13
- ```
14
+
14
15
  2. Copy the contents of [src/misc/install.diff](src/misc/install.diff) to your local folder
16
+ ```sh
17
+ curl https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/misc/install.diff > install.diff
15
18
  ```
16
- $ curl https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/misc/install.diff > install.diff
17
- ```
19
+
18
20
  3. Apply the diff to carry out all the required changes
21
+ ```sh
22
+ git apply install.diff
19
23
  ```
20
- $ git apply install.diff
21
- ```
24
+
22
25
  4. Make sure to look through the applied changes and remove the diff file before committing.
23
26
 
27
+ 5. Fill in the configuration options in `server/middleware/setUpPrisonerAuth.ts`:
28
+
29
+ See [PrisonerAuthOptions](https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/main/prisonerAuth.ts) for full documentation on the avaialble options
30
+
24
31
  ### Manual installation
25
32
 
33
+ NOTE: see [install.diff](https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/misc/install.diff) for an authorititive list of changes to be made.
34
+
26
35
  1. Add the package `@ministryofjustice/hmpps-prisoner-auth` to your `package.json`
36
+ ```sh
37
+ npm add --save @ministryofjustice/hmpps-prisoner-auth
38
+ npm i
27
39
  ```
28
- $ npm add --save @ministryofjustice/hmpps-prisoner-auth
29
- $ npm i
30
- ```
40
+
31
41
  2. Modify `interfaces/hmppsUser.ts` to make the following additions:
32
- ```
42
+ ```ts
33
43
  # add prisoner-auth to the AuthSource type
34
44
  export type AuthSource = 'nomis' | 'delius' | 'external' | 'azuread' | 'prisoner-auth'
35
45
 
@@ -38,87 +48,16 @@ export type HmppsUser = PrisonUser | ProbationUser | ExternalUser | AzureADUser
38
48
  ```
39
49
  These changes will help smooth over alot of assumptions made in the typescript template about the type of fields the user will have.
40
50
 
41
- 3. Modify `server/middleware/setupAuthentication.ts` to add prisoner auth as the passport strategy (see below for the full list of config options):
42
- ```
43
- passport.use(
44
- 'prisoner-auth',
45
- prisonerAuthStrategy(
46
- {
47
- launchpadAuthUrl: 'https://launchpad.instance.etc' ...
48
- }
49
- )
50
- )
51
- ```
52
- Note: it is recommended to give the strategy a name ('prisoner-auth' here) for later referral with passport.authenticate middleware.
51
+ 3. Add `server/middleware/setUpPrisonerAuth.ts` middlware:
53
52
 
54
- 4. Modify `server/middleware/setupAuthentication.ts` to remove any hmpps auth specific code and add in prisoner auth setup. The following code snippet is intended as a guide:
55
- ```
56
- export default function setupAuthentication() {
57
- const router = Router()
58
-
59
- router.use(passport.initialize())
60
- router.use(passport.session())
61
- router.use(flash())
62
-
63
- router.get('/autherror', (req, res) => {
64
- res.status(401)
65
- return res.render('autherror')
66
- })
67
-
68
- router.get('/sign-in', passport.authenticate('prisoner-auth'))
69
-
70
- router.get('/sign-in/callback', (req, res, next) =>
71
- passport.authenticate('prisoner-auth', {
72
- successReturnToOrRedirect: req.session.returnTo || '/',
73
- failureRedirect: '/autherror',
74
- })(req, res, next),
75
- )
76
-
77
- router.use('/sign-out', (req, res, next) => {
78
- if (req.user) {
79
- req.logout(err => {
80
- if (err) return next(err)
81
- return req.session.destroy(() => res.redirect('/'))
82
- })
83
- } else res.redirect('/')
84
- })
85
-
86
- router.use(async (req, res, next) => {
87
- if (!req.isAuthenticated()) {
88
- req.session.returnTo = req.originalUrl
89
- return res.redirect('/sign-in')
90
- }
91
-
92
- return prisonerAuth
93
- .validateAndRefreshUser(req.user as LaunchpadUser)
94
- .then(user => {
95
- req.user = user
96
- next()
97
- })
98
- .catch(() => res.redirect('/autherror'))
99
- })
100
-
101
- router.use((req, res, next) => {
102
- res.locals.user = req.user as HmppsUser
103
- next()
104
- })
105
-
106
- return router
107
- }
53
+ ```sh
54
+ curl https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/misc/setUpPrisonerAuth.ts.example > server/middleware/setUpPrisonerAuth.ts
108
55
  ```
109
- 5. You may wish to remove the following middleware from `server/app.ts` as they were not designed for use with `LaunchpadUser`:
110
- - `authorisationMiddleware()` - this checks the users `ROLE_` which a launchpad user will not have.
111
- - `setUpCurrentUser()` - this sets up the `res.locals.user` fields which a launchpad will have already (`name`, `displayName` etc).
112
-
113
- ### Configuration Options
114
-
115
- | Option | Description |
116
- | - | - |
117
- | launchpadAuthUrl | The full URL of the launchpad auth instance you wish to authenticate against |
118
- | clientID | The client ID |
119
- | clientSecret | The client secret |
120
- | callbackURL | *Optional, The URL or path you wish launchpad to call back to in the auth flow. Defaults to `/signin/callback` |
121
- | scope | *Optional, One or more OpenIdConnect Scopes you require for the application. Defaults to `['user.basic.read', 'user.establishment.read', 'user.booking.read']` |
122
- | tokenMinimumLifespan | The least amount of time a token must have left before it is considered expired. Expressed as a TimeSpan. eg. `minutes(5)` or `nothing()`. |
123
-
124
- See `PrisonerAuthOptions` for full list and documentation for these options.
56
+ Make sure to fill in the appropriate values.
57
+
58
+ See [PrisonerAuthOptions](https://raw.githubusercontent.com/ministryofjustice/hmpps-prisoner-facing-typescript-lib/refs/heads/main/packages/prisoner-auth/src/main/prisonerAuth.ts) for full documentation on the avaialble options
59
+
60
+ 4. Modify `server/app.ts`:
61
+ - ADD `setUpPrisonerAuth()` to enable the middlware.
62
+ - REMOVE reference to `authorisationMiddleware()` - this checks the users `ROLE_` which a launchpad user will not have.
63
+ - REMOVE reference to `setUpCurrentUser()` - this sets up the `res.locals.user` fields which a launchpad will have already (`name`, `displayName` etc).
package/dist/index.cjs.js CHANGED
@@ -99,11 +99,13 @@ class TimeSpan {
99
99
  }
100
100
  }
101
101
 
102
+ const nullOnLoginSuccessCallback = _ => Promise.resolve();
102
103
  class PrisonerAuth {
103
104
  launchpadAuthUrl;
104
105
  authorizationUrl;
105
106
  tokenUrl;
106
107
  callbackUrl;
108
+ onLoginSuccessCallback;
107
109
  clientId;
108
110
  clientSecret;
109
111
  scope;
@@ -115,6 +117,7 @@ class PrisonerAuth {
115
117
  this.authorizationUrl = options.authorizationUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/authorize`;
116
118
  this.tokenUrl = options.tokenUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/token`;
117
119
  this.callbackUrl = options.callbackUrl ?? '/sign-in/callback';
120
+ this.onLoginSuccessCallback = options.onLoginSuccessCallback ?? nullOnLoginSuccessCallback;
118
121
  this.clientId = options.clientId;
119
122
  this.clientSecret = options.clientSecret;
120
123
  this.scope = options.scope ?? ['user.basic.read', 'user.establishment.read', 'user.booking.read'];
@@ -139,7 +142,11 @@ class PrisonerAuth {
139
142
  skipUserProfile: true,
140
143
  nonce: this.nonce ? 'true' : undefined,
141
144
  customHeaders: { authorization: this.authorizationToken() },
142
- }, ((_issuer, _profile, _context, idToken, accessToken, refreshToken, done) => done(null, userFromTokens({ idToken, accessToken, refreshToken }))));
145
+ }, (async (_issuer, _profile, _context, idToken, accessToken, refreshToken, done) => {
146
+ const user = userFromTokens({ idToken, accessToken, refreshToken });
147
+ await this.onLoginSuccessCallback(user);
148
+ done(null, user);
149
+ }));
143
150
  }
144
151
  /**
145
152
  * Checks the expiry of a given LaunchpadUser idToken and refreshToken.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/main/tokens.ts","../src/main/launchpadUser.ts","../src/main/timeSpans.ts","../src/main/prisonerAuth.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/main/tokens.ts","../src/main/launchpadUser.ts","../src/main/timeSpans.ts","../src/main/prisonerAuth.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -85,6 +85,10 @@ type PrisonerAuthOptions = {
85
85
  * If omitted, callbackUrl will default to /sign-in/callback
86
86
  */
87
87
  callbackUrl?: string;
88
+ /**
89
+ * Optional: A callback that will be invoked on sucessful login, provides the logged in user as a parameter
90
+ */
91
+ onLoginSuccessCallback?: OnLoginSuccessCallback;
88
92
  /** The Client ID of this application to send to Launchpad Auth */
89
93
  clientId: string;
90
94
  /** The Client Secret of this application to send to Launchpad Auth */
@@ -126,11 +130,13 @@ type PrisonerAuthOptions = {
126
130
  */
127
131
  tokenFetcher?: TokenFetcher;
128
132
  };
133
+ type OnLoginSuccessCallback = (user: LaunchpadUser) => Promise<void>;
129
134
  declare class PrisonerAuth {
130
135
  readonly launchpadAuthUrl: string;
131
136
  readonly authorizationUrl: string;
132
137
  readonly tokenUrl: string;
133
138
  readonly callbackUrl: string;
139
+ readonly onLoginSuccessCallback: OnLoginSuccessCallback;
134
140
  readonly clientId: string;
135
141
  readonly clientSecret: string;
136
142
  readonly scope: Scope | Scope[];
package/dist/index.esm.js CHANGED
@@ -97,11 +97,13 @@ class TimeSpan {
97
97
  }
98
98
  }
99
99
 
100
+ const nullOnLoginSuccessCallback = _ => Promise.resolve();
100
101
  class PrisonerAuth {
101
102
  launchpadAuthUrl;
102
103
  authorizationUrl;
103
104
  tokenUrl;
104
105
  callbackUrl;
106
+ onLoginSuccessCallback;
105
107
  clientId;
106
108
  clientSecret;
107
109
  scope;
@@ -113,6 +115,7 @@ class PrisonerAuth {
113
115
  this.authorizationUrl = options.authorizationUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/authorize`;
114
116
  this.tokenUrl = options.tokenUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/token`;
115
117
  this.callbackUrl = options.callbackUrl ?? '/sign-in/callback';
118
+ this.onLoginSuccessCallback = options.onLoginSuccessCallback ?? nullOnLoginSuccessCallback;
116
119
  this.clientId = options.clientId;
117
120
  this.clientSecret = options.clientSecret;
118
121
  this.scope = options.scope ?? ['user.basic.read', 'user.establishment.read', 'user.booking.read'];
@@ -137,7 +140,11 @@ class PrisonerAuth {
137
140
  skipUserProfile: true,
138
141
  nonce: this.nonce ? 'true' : undefined,
139
142
  customHeaders: { authorization: this.authorizationToken() },
140
- }, ((_issuer, _profile, _context, idToken, accessToken, refreshToken, done) => done(null, userFromTokens({ idToken, accessToken, refreshToken }))));
143
+ }, (async (_issuer, _profile, _context, idToken, accessToken, refreshToken, done) => {
144
+ const user = userFromTokens({ idToken, accessToken, refreshToken });
145
+ await this.onLoginSuccessCallback(user);
146
+ done(null, user);
147
+ }));
141
148
  }
142
149
  /**
143
150
  * Checks the expiry of a given LaunchpadUser idToken and refreshToken.
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/main/tokens.ts","../src/main/launchpadUser.ts","../src/main/timeSpans.ts","../src/main/prisonerAuth.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/main/tokens.ts","../src/main/launchpadUser.ts","../src/main/timeSpans.ts","../src/main/prisonerAuth.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
@@ -23,6 +23,10 @@ export type PrisonerAuthOptions = {
23
23
  * If omitted, callbackUrl will default to /sign-in/callback
24
24
  */
25
25
  callbackUrl?: string;
26
+ /**
27
+ * Optional: A callback that will be invoked on sucessful login, provides the logged in user as a parameter
28
+ */
29
+ onLoginSuccessCallback?: OnLoginSuccessCallback;
26
30
  /** The Client ID of this application to send to Launchpad Auth */
27
31
  clientId: string;
28
32
  /** The Client Secret of this application to send to Launchpad Auth */
@@ -64,11 +68,13 @@ export type PrisonerAuthOptions = {
64
68
  */
65
69
  tokenFetcher?: TokenFetcher;
66
70
  };
71
+ export type OnLoginSuccessCallback = (user: LaunchpadUser) => Promise<void>;
67
72
  export default class PrisonerAuth {
68
73
  readonly launchpadAuthUrl: string;
69
74
  readonly authorizationUrl: string;
70
75
  readonly tokenUrl: string;
71
76
  readonly callbackUrl: string;
77
+ readonly onLoginSuccessCallback: OnLoginSuccessCallback;
72
78
  readonly clientId: string;
73
79
  readonly clientSecret: string;
74
80
  readonly scope: Scope | Scope[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ministryofjustice/hmpps-prisoner-auth",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Middleware for providing authentication against Launchpad Auth",
5
5
  "author": "hmpps-launchpad-devs",
6
6
  "license": "MIT",
@@ -18,8 +18,7 @@
18
18
  "types": "./dist/index.d.ts",
19
19
  "files": [
20
20
  "*.md",
21
- "dist/**/*",
22
- "src/**/*"
21
+ "dist/**/*"
23
22
  ],
24
23
  "engines": {
25
24
  "node": "24"
@@ -42,5 +41,7 @@
42
41
  "passport-openidconnect": "^0.1.2",
43
42
  "superagent": "^10.3.0"
44
43
  },
45
- "peerDependencies": {}
44
+ "peerDependencies": {
45
+ "express": "^5.2.1"
46
+ }
46
47
  }
package/src/main/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { default as PrisonerAuth, type PrisonerAuthOptions } from './prisonerAuth'
2
- export { minutes, seconds, milliseconds, nothing, TimeSpan } from './timeSpans'
3
- export type { LaunchpadUser } from './launchpadUser'
@@ -1,41 +0,0 @@
1
- import jwt from 'jsonwebtoken'
2
- import { userFromTokens } from './launchpadUser'
3
-
4
- describe('userFromTokens', () => {
5
- const aJwtOf = (obj: object): string => jwt.sign(obj, 'secret')
6
-
7
- const idToken = aJwtOf({
8
- name: 'Test User',
9
- given_name: 'Test',
10
- family_name: 'User',
11
- establishment: { agency_id: 'CKI' },
12
- sub: '12345',
13
- })
14
- const accessToken = aJwtOf({ hello: 'world' })
15
- const refreshToken = aJwtOf({ hello: 'world' })
16
-
17
- it('sets the given tokens on the user for later use (refreshing tokens)', () => {
18
- const result = userFromTokens({ idToken, accessToken, refreshToken })
19
-
20
- expect(result.idToken).toEqual(idToken)
21
- expect(result.accessToken).toEqual(accessToken)
22
- expect(result.refreshToken).toEqual(refreshToken)
23
- })
24
-
25
- it('sets the establishment from the idToken', () => {
26
- const result = userFromTokens({ idToken, accessToken, refreshToken })
27
- expect(result.establishment).toEqual({ agency_id: 'CKI' })
28
- })
29
-
30
- it('sets the name fields from the idToken', () => {
31
- const result = userFromTokens({ idToken, accessToken, refreshToken })
32
- expect(result.name).toEqual('Test User')
33
- expect(result.givenName).toEqual('Test')
34
- expect(result.familyName).toEqual('User')
35
- })
36
-
37
- it('sets authSource to give compatibility with the HmppsUser interface', () => {
38
- const result = userFromTokens({ idToken, accessToken, refreshToken })
39
- expect(result.authSource).toEqual('prisoner-auth')
40
- })
41
- })
@@ -1,42 +0,0 @@
1
- import { IdToken, RawTokens, tokenFromJwt } from './tokens'
2
-
3
- export type LaunchpadUser = {
4
- authSource: 'prisoner-auth'
5
- idToken: string
6
- refreshToken: string
7
- accessToken: string
8
- establishment: IdToken['establishment']
9
- name: string
10
- givenName: string
11
- familyName: string
12
-
13
- // The following fields are for compatibility with HmppsUser only
14
- token: string
15
- username: string
16
- userId: string
17
- displayName: string
18
- userRoles: string[]
19
- }
20
-
21
- export const userFromTokens = ({ idToken, accessToken, refreshToken }: RawTokens): LaunchpadUser => {
22
- const { establishment, name, given_name: givenName, family_name: familyName, sub } = tokenFromJwt<IdToken>(idToken)
23
- const authSource = 'prisoner-auth'
24
-
25
- return {
26
- authSource,
27
- idToken,
28
- refreshToken,
29
- accessToken,
30
- establishment,
31
- name,
32
- givenName,
33
- familyName,
34
-
35
- // The following fields are for compatibility with HmppsUser only
36
- token: idToken,
37
- username: name,
38
- userId: sub,
39
- displayName: name,
40
- userRoles: [],
41
- }
42
- }
@@ -1,117 +0,0 @@
1
- import { minutes, timeAgo, timeFromNow } from './timeSpans'
2
- import PrisonerAuth, { PrisonerAuthOptions } from './prisonerAuth'
3
- import { LaunchpadUser } from './launchpadUser'
4
- import { AccessToken, IdToken, RefreshToken, tokenFromJwt } from './tokens'
5
- import { aJwtOf, aUserWith } from './testUtils'
6
-
7
- describe('PrisonerAuth', () => {
8
- const options: PrisonerAuthOptions = {
9
- launchpadAuthUrl: 'https://launchpad.example.com',
10
- clientId: 'cf837899-2a3c-4838-aa83-5fb76bd58df1',
11
- clientSecret: '445f30b2-1010-4bcb-8cba-447b70463bd0',
12
- tokenMinimumLifespan: minutes(5),
13
- }
14
-
15
- describe('setting options', () => {
16
- it('sets sane defaults for the URL options', () => {
17
- const prisonerAuth = new PrisonerAuth(options)
18
- expect(prisonerAuth.authorizationUrl).toEqual('https://launchpad.example.com/v1/oauth2/authorize')
19
- expect(prisonerAuth.tokenUrl).toEqual('https://launchpad.example.com/v1/oauth2/token')
20
- })
21
-
22
- it('allows custom override of the URL options', () => {
23
- const prisonerAuth = new PrisonerAuth({
24
- ...options,
25
- authorizationUrl: 'https://auth.com',
26
- tokenUrl: 'https://token.com',
27
- })
28
- expect(prisonerAuth.authorizationUrl).toEqual('https://auth.com')
29
- expect(prisonerAuth.tokenUrl).toEqual('https://token.com')
30
- })
31
-
32
- it('sets the client credentials', () => {
33
- const prisonerAuth = new PrisonerAuth(options)
34
- expect(prisonerAuth.clientId).toEqual('cf837899-2a3c-4838-aa83-5fb76bd58df1')
35
- expect(prisonerAuth.clientSecret).toEqual('445f30b2-1010-4bcb-8cba-447b70463bd0')
36
- })
37
-
38
- it('allows overriding of the callbackUrl', () => {
39
- const prisonerAuth = new PrisonerAuth({ ...options, callbackUrl: '/my/call/back' })
40
- expect(prisonerAuth.callbackUrl).toEqual('/my/call/back')
41
- })
42
-
43
- it('sets a default callbackUrl when none provided', () => {
44
- const prisonerAuth = new PrisonerAuth(options)
45
- expect(prisonerAuth.callbackUrl).toEqual('/sign-in/callback')
46
- })
47
-
48
- it('allows overriding of the scopes', () => {
49
- const prisonerAuth = new PrisonerAuth({ ...options, scope: 'user.basic.read' })
50
- expect(prisonerAuth.scope).toEqual('user.basic.read')
51
- })
52
-
53
- it('sets a default scope of all read only scopes when no scopes given', () => {
54
- const prisonerAuth = new PrisonerAuth(options)
55
- expect(prisonerAuth.scope).toEqual(['user.basic.read', 'user.establishment.read', 'user.booking.read'])
56
- })
57
- })
58
-
59
- describe('validateAndRefreshUser', () => {
60
- describe('when the users idToken has not yet expired', () => {
61
- it('returns the user as it is', async () => {
62
- const prisonerAuth = new PrisonerAuth(options)
63
- const user = aUserWith({
64
- idTokenExp: timeFromNow(minutes(6)).seconds,
65
- refreshTokenExp: timeFromNow(minutes(6)).seconds,
66
- })
67
-
68
- const result = await prisonerAuth.validateAndRefreshUser(user as LaunchpadUser)
69
- const idToken = tokenFromJwt<IdToken>(result.idToken)
70
-
71
- expect(idToken.iat).toEqual(123_456_789)
72
- })
73
- })
74
-
75
- describe('when the users idToken has expired', () => {
76
- describe('when the users refreshToken has not yet expired', () => {
77
- it('returns a new set of user tokens for the given user', async () => {
78
- const prisonerAuth = new PrisonerAuth({
79
- ...options,
80
- tokenFetcher: jest.fn().mockResolvedValue({
81
- idToken: aJwtOf({ iat: 987_654_321 }),
82
- refreshToken: aJwtOf({ iat: 987_654_322 }),
83
- accessToken: aJwtOf({ iat: 987_654_323 }),
84
- }),
85
- })
86
- const user = aUserWith({
87
- idTokenExp: timeAgo(minutes(3)).seconds,
88
- refreshTokenExp: timeFromNow(minutes(6)).seconds,
89
- })
90
-
91
- const result = await prisonerAuth.validateAndRefreshUser(user as LaunchpadUser)
92
- const idToken = tokenFromJwt<IdToken>(result.idToken)
93
- const refreshToken = tokenFromJwt<RefreshToken>(result.refreshToken)
94
- const accessToken = tokenFromJwt<AccessToken>(result.accessToken)
95
-
96
- expect(idToken.iat).toEqual(987_654_321)
97
- expect(refreshToken.iat).toEqual(987_654_322)
98
- expect(accessToken.iat).toEqual(987_654_323)
99
- })
100
- })
101
-
102
- describe('when the users refreshToken has expired', () => {
103
- it('throws an error as we cant continue', async () => {
104
- const prisonerAuth = new PrisonerAuth(options)
105
- const user = aUserWith({
106
- idTokenExp: timeAgo(minutes(3)).seconds,
107
- refreshTokenExp: timeFromNow(minutes(3)).seconds,
108
- })
109
-
110
- await expect(async () => {
111
- await prisonerAuth.validateAndRefreshUser(user as LaunchpadUser)
112
- }).rejects.toThrow('Refresh token expired')
113
- })
114
- })
115
- })
116
- })
117
- })
@@ -1,174 +0,0 @@
1
- import OpenIDConnectStrategy, { Profile, VerifyCallback, VerifyFunction } from 'passport-openidconnect'
2
- import { IdToken, oauthClientToken, RefreshToken, Scope, tokenFromJwt, tokenFetcher, TokenFetcher } from './tokens'
3
- import { LaunchpadUser, userFromTokens } from './launchpadUser'
4
- import { seconds, timeFromNow, TimeSpan } from './timeSpans'
5
-
6
- export type PrisonerAuthOptions = {
7
- /** The full URL of the Launchpad Auth instance */
8
- launchpadAuthUrl: string
9
-
10
- /**
11
- * Optional: The full URL of the authorisation path on the Launchpad Auth instance.
12
- *
13
- * If omitted, authorizationUrl will default to $launchpadAuthUrl/v1/oauth2/authorize
14
- */
15
- authorizationUrl?: string
16
-
17
- /**
18
- * Optional: The full URL of the token path on the Launchpad Auth instance.
19
- *
20
- * If omitted, tokenUrl will default to $launchpadAuthUrl/v1/oauth2/token
21
- */
22
- tokenUrl?: string
23
-
24
- /**
25
- * Optional: The URL or path that Launchpad Auth will be told to call back to with the tokens.
26
- *
27
- * If omitted, callbackUrl will default to /sign-in/callback
28
- */
29
- callbackUrl?: string
30
-
31
- /** The Client ID of this application to send to Launchpad Auth */
32
- clientId: string
33
- /** The Client Secret of this application to send to Launchpad Auth */
34
- clientSecret: string
35
- /**
36
- * Optional: The scope or scopes to request on behalf of the user.
37
- *
38
- * If omitted, scope will default to user.basic.read, user.establishment.read and user.booking.read
39
- */
40
- scope?: Scope | Scope[]
41
- /**
42
- * Optional: Whether or not to use nonce, not recommended to be disabled except for integration tests
43
- *
44
- * If ommitted. defaults to true
45
- */
46
- nonce?: boolean
47
-
48
- /**
49
- * A time span specifying the least amount of time a token must have left before it is considered expired.
50
- *
51
- * @example
52
- *
53
- * Token will be considered expired when its exp value is in the past.
54
- * ```ts
55
- * nothing()
56
- * ```
57
- *
58
- * Token will be considered expired unless its exp value is **AT LEAST 5** minutes in the future from time of checking.
59
- * ```ts
60
- * minutes(5)
61
- * ```
62
- *
63
- * @see TimeSpan
64
- */
65
- tokenMinimumLifespan: TimeSpan
66
-
67
- /**
68
- * Optional: Token fetcher implementation. Intended for unit testing PrisonerAuth
69
- *
70
- * @see TokenFetcher
71
- */
72
- tokenFetcher?: TokenFetcher
73
- }
74
-
75
- export default class PrisonerAuth {
76
- readonly launchpadAuthUrl: string
77
-
78
- readonly authorizationUrl: string
79
-
80
- readonly tokenUrl: string
81
-
82
- readonly callbackUrl: string
83
-
84
- readonly clientId: string
85
-
86
- readonly clientSecret: string
87
-
88
- readonly scope: Scope | Scope[]
89
-
90
- readonly nonce: boolean
91
-
92
- readonly tokenMinimumLifespan: TimeSpan
93
-
94
- readonly tokenFetcher: TokenFetcher
95
-
96
- constructor(options: PrisonerAuthOptions) {
97
- this.launchpadAuthUrl = options.launchpadAuthUrl
98
- this.authorizationUrl = options.authorizationUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/authorize`
99
- this.tokenUrl = options.tokenUrl ?? `${options.launchpadAuthUrl}/v1/oauth2/token`
100
- this.callbackUrl = options.callbackUrl ?? '/sign-in/callback'
101
- this.clientId = options.clientId
102
- this.clientSecret = options.clientSecret
103
- this.scope = options.scope ?? ['user.basic.read', 'user.establishment.read', 'user.booking.read']
104
- this.nonce = options.nonce ?? true
105
- this.tokenMinimumLifespan = options.tokenMinimumLifespan
106
- this.tokenFetcher = options.tokenFetcher ?? tokenFetcher
107
- }
108
-
109
- /**
110
- *
111
- * @returns OpenIDConnectStrategy ready for use with passport
112
- */
113
- passportStrategy(): OpenIDConnectStrategy {
114
- return new OpenIDConnectStrategy(
115
- {
116
- issuer: this.launchpadAuthUrl,
117
- authorizationURL: this.authorizationUrl,
118
- tokenURL: this.tokenUrl,
119
- callbackURL: this.callbackUrl,
120
- clientID: this.clientId,
121
- clientSecret: this.clientSecret,
122
- scope: this.scope,
123
- userInfoURL: '',
124
- skipUserProfile: true,
125
- nonce: this.nonce ? 'true' : undefined,
126
- customHeaders: { authorization: this.authorizationToken() },
127
- },
128
- ((
129
- _issuer: string,
130
- _profile: Profile,
131
- _context: object,
132
- idToken: string,
133
- accessToken: string,
134
- refreshToken: string,
135
- done: VerifyCallback,
136
- ) => done(null, userFromTokens({ idToken, accessToken, refreshToken }))) as VerifyFunction,
137
- )
138
- }
139
-
140
- /**
141
- * Checks the expiry of a given LaunchpadUser idToken and refreshToken.
142
- * - If the idToken has expired it will attempt to refresh it using the refreshToken.
143
- * - If the refreshToken has expired it will throw an Error.
144
- *
145
- * This method will always return a LaunchpadUser so it is suggested to updated your local copy and it will keep the tokens up to date.
146
- *
147
- * @param user - The LaunchpadUser instance to check token expiry on.
148
- * @returns A LaunchpadUser is always returned, either the given user or a user with refreshed tokens.
149
- * @throws Error is thrown if the refreshToken has expired, the user must sign in again from scratch.
150
- */
151
- async validateAndRefreshUser(user: LaunchpadUser): Promise<LaunchpadUser> {
152
- const idToken = tokenFromJwt<IdToken>(user.idToken)
153
- const refreshToken = tokenFromJwt<RefreshToken>(user.refreshToken)
154
- const tokenExpiryTime = timeFromNow(this.tokenMinimumLifespan)
155
-
156
- // Id Token still valid, return the current user
157
- if (seconds(idToken.exp).isGreaterThanOrEqualTo(tokenExpiryTime)) {
158
- return user
159
- }
160
-
161
- // Refresh token expired, cannot continue
162
- if (seconds(refreshToken.exp).isLessThan(tokenExpiryTime)) {
163
- throw new Error('Refresh token expired')
164
- }
165
-
166
- // Refresh the user tokens
167
- const newTokens = await this.tokenFetcher(user.refreshToken, this.tokenUrl, this.authorizationToken())
168
- return userFromTokens(newTokens)
169
- }
170
-
171
- private authorizationToken(): string {
172
- return oauthClientToken(this.clientId, this.clientSecret)
173
- }
174
- }
@@ -1,10 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
- import jwt from 'jsonwebtoken'
3
-
4
- export const aJwtOf = (obj: object): string => jwt.sign(obj, 'secret')
5
-
6
- export const aUserWith = ({ idTokenExp, refreshTokenExp }: { idTokenExp: number; refreshTokenExp: number }) => ({
7
- idToken: aJwtOf({ exp: idTokenExp, iat: 123_456_789, sub: 'USER_A' }),
8
- refreshToken: aJwtOf({ exp: refreshTokenExp, iat: 123_456_789, sub: 'USER_A' }),
9
- accessToken: aJwtOf({ exp: refreshTokenExp, iat: 123_456_789, sub: 'USER_A' }),
10
- })
@@ -1,129 +0,0 @@
1
- import { milliseconds, minutes, seconds, timeAgo, timeFromNow, TimeSpan } from './timeSpans'
2
-
3
- describe('time spans', () => {
4
- describe('minutes', () => {
5
- const cases = [
6
- [1, 1, 60, 60000],
7
- [5, 5, 300, 300000],
8
- [0.5, 0.5, 30, 30000],
9
- [0, 0, 0, 0],
10
- ]
11
-
12
- test.each(cases)(
13
- 'Given %p minute(s), minutes is %p, seconds is %p and milliseconds is %p',
14
- (example, expectedMinutes, expectedSeconds, expectedMilliseconds) => {
15
- const subject = minutes(example)
16
- expect(subject.minutes).toEqual(expectedMinutes)
17
- expect(subject.seconds).toEqual(expectedSeconds)
18
- expect(subject.milliseconds).toEqual(expectedMilliseconds)
19
- },
20
- )
21
- })
22
-
23
- describe('seconds', () => {
24
- const cases = [
25
- [1, 0.01666667, 1, 1000],
26
- [60, 1, 60, 60000],
27
- [120, 2, 120, 120000],
28
- ]
29
-
30
- test.each(cases)(
31
- 'Given %p seconds(s), minutes is %p, seconds is %p and milliseconds is %p',
32
- (example, expectedMinutes, expectedSeconds, expectedMilliseconds) => {
33
- const subject = seconds(example)
34
- expect(subject.minutes).toBeCloseTo(expectedMinutes)
35
- expect(subject.seconds).toEqual(expectedSeconds)
36
- expect(subject.milliseconds).toEqual(expectedMilliseconds)
37
- },
38
- )
39
- })
40
-
41
- describe('milliseconds', () => {
42
- const cases = [
43
- [1, 0.00001667, 0.001, 1],
44
- [1000, 0.01666667, 1, 1000],
45
- ]
46
-
47
- test.each(cases)(
48
- 'Given %p milliseconds(s), minutes is %p, seconds is %p and milliseconds is %p',
49
- (example, expectedMinutes, expectedSeconds, expectedMilliseconds) => {
50
- const subject = milliseconds(example)
51
- expect(subject.minutes).toBeCloseTo(expectedMinutes)
52
- expect(subject.seconds).toEqual(expectedSeconds)
53
- expect(subject.milliseconds).toEqual(expectedMilliseconds)
54
- },
55
- )
56
- })
57
-
58
- describe('isEqualTo', () => {
59
- const cases: [string, TimeSpan, TimeSpan, boolean][] = [
60
- ['60 seconds is equal to 1 minute', seconds(60), minutes(1), true],
61
- ['120 seconds is equal to 1 and a half minutes', seconds(90), minutes(1.5), true],
62
- ['2 and a half seconds is equal to 2500 milliseconds', seconds(2.5), milliseconds(2500), true],
63
- ['120 minutes is equal to 7200 seconds', minutes(120), seconds(7200), true],
64
- ['120 minutes is not equal to 7000 seconds', minutes(120), seconds(7000), false],
65
- ['1 seconds is not equal to 1 minutes', seconds(1), minutes(1), false],
66
- ]
67
-
68
- test.each(cases)('%s', (testCaseDescription, example1, example2, expectedResult) => {
69
- expect(example1.isEqualTo(example2)).toBe(expectedResult)
70
- })
71
- })
72
-
73
- describe('isGreaterThan', () => {
74
- const cases: [string, TimeSpan, TimeSpan, boolean][] = [
75
- ['61 seconds is greater than 1 minute', seconds(61), minutes(1), true],
76
- ['125 seconds is greater than 1 and a half minutes', seconds(121), minutes(1.5), true],
77
- ['60 seconds is not greater than 1 minute', seconds(60), minutes(1), false],
78
- ['1 minute is not greater than 61 seconds', minutes(1), seconds(61), false],
79
- ]
80
-
81
- test.each(cases)('%s', (testCaseDescription, example1, example2, expectedResult) => {
82
- expect(example1.isGreaterThan(example2)).toBe(expectedResult)
83
- })
84
- })
85
-
86
- describe('timeAgo', () => {
87
- const dateNowInMs: number = 1770000000000
88
-
89
- beforeEach(() => {
90
- Date.now = jest.fn(() => dateNowInMs)
91
- })
92
-
93
- const cases: [string, number, TimeSpan][] = [
94
- ['1 millisecond', dateNowInMs - 1, milliseconds(1)],
95
- ['1 second', dateNowInMs - 1000, seconds(1)],
96
- ['30 seconds', dateNowInMs - 30000, seconds(30)],
97
- ['2 minutes', dateNowInMs - 120000, minutes(2)],
98
- ]
99
-
100
- test.each(cases)(
101
- 'Given a current time in milliseconds 1770000000000 and a timespan of %p, the expected past timespan in milliseconds is %p',
102
- (description, expectedMilliseconds, example) => {
103
- expect(timeAgo(example).milliseconds).toEqual(expectedMilliseconds)
104
- },
105
- )
106
- })
107
-
108
- describe('timeFromNow', () => {
109
- const dateNowInMs: number = 1770000000000
110
-
111
- beforeEach(() => {
112
- Date.now = jest.fn(() => dateNowInMs)
113
- })
114
-
115
- const cases: [string, number, TimeSpan][] = [
116
- ['1 millisecond', dateNowInMs + 1, milliseconds(1)],
117
- ['1 second', dateNowInMs + 1000, seconds(1)],
118
- ['30 seconds', dateNowInMs + 30000, seconds(30)],
119
- ['2 minutes', dateNowInMs + 120000, minutes(2)],
120
- ]
121
-
122
- test.each(cases)(
123
- 'Given a current time in milliseconds 1770000000000 and a timespan of %p, the expected future timespan in milliseconds is %p',
124
- (description, expectedMilliseconds, example) => {
125
- expect(timeFromNow(example).milliseconds).toEqual(expectedMilliseconds)
126
- },
127
- )
128
- })
129
- })
@@ -1,74 +0,0 @@
1
- // A TimeSpan allows you to give meaning to a number and then read that number back and compare them in different formats
2
- // ie.
3
- // seconds(60).minutes === 1
4
- // seconds(30).seconds === 30
5
- // minutes(1).seconds === 60
6
- // milliseconds(1000).seconds === 1
7
- // milliseconds(2000).isEqualTo(seconds(2)) #=> true
8
- // minutes(5).
9
- // This allows a caller to just accept a TimeSpan and then work in the units it cares about
10
-
11
- export const minutes = (numMinutes: number): TimeSpan => TimeSpan.minutes(numMinutes)
12
-
13
- export const seconds = (numSeconds: number): TimeSpan => TimeSpan.seconds(numSeconds)
14
-
15
- export const milliseconds = (numMilliseconds: number): TimeSpan => TimeSpan.milliseconds(numMilliseconds)
16
-
17
- export const nothing = (): TimeSpan => TimeSpan.nothing()
18
-
19
- // Get a time span in the past
20
- export const timeAgo = (ago: TimeSpan) => milliseconds(Date.now() - ago.milliseconds)
21
-
22
- // Get a time span in the future
23
- export const timeFromNow = (fromNow: TimeSpan) => milliseconds(Date.now() + fromNow.milliseconds)
24
-
25
- // Implementation
26
- export class TimeSpan {
27
- readonly minutes: number
28
-
29
- readonly seconds: number
30
-
31
- readonly milliseconds: number
32
-
33
- private constructor(numMinutes: number, numSeconds: number, numMilliseconds: number) {
34
- this.minutes = numMinutes
35
- this.seconds = numSeconds
36
- this.milliseconds = numMilliseconds
37
- }
38
-
39
- static minutes(numMinutes: number): TimeSpan {
40
- return new TimeSpan(numMinutes, numMinutes * 60, TimeSpan.seconds(numMinutes * 60).milliseconds)
41
- }
42
-
43
- static seconds(numSeconds: number): TimeSpan {
44
- return new TimeSpan(numSeconds / 60, numSeconds, numSeconds * 1000)
45
- }
46
-
47
- static milliseconds(numMilliseconds: number): TimeSpan {
48
- return new TimeSpan(TimeSpan.seconds(numMilliseconds / 1000).minutes, numMilliseconds / 1000, numMilliseconds)
49
- }
50
-
51
- static nothing(): TimeSpan {
52
- return new TimeSpan(0, 0, 0)
53
- }
54
-
55
- isGreaterThan(t: TimeSpan): boolean {
56
- return this.milliseconds > t.milliseconds
57
- }
58
-
59
- isGreaterThanOrEqualTo(t: TimeSpan): boolean {
60
- return this.milliseconds >= t.milliseconds
61
- }
62
-
63
- isLessThan(t: TimeSpan): boolean {
64
- return this.milliseconds < t.milliseconds
65
- }
66
-
67
- isLessThanOrEqualTo(t: TimeSpan): boolean {
68
- return this.milliseconds <= t.milliseconds
69
- }
70
-
71
- isEqualTo(t: TimeSpan): boolean {
72
- return this.milliseconds === t.milliseconds
73
- }
74
- }
@@ -1,65 +0,0 @@
1
- import superagent from 'superagent'
2
-
3
- export type IdToken = {
4
- name: string
5
- given_name: string
6
- family_name: string
7
- nonce?: string
8
- iat: number
9
- aud: string
10
- sub: string
11
- exp: number // exp is in seconds
12
- booking: { id: string }
13
- establishment: {
14
- agency_id: string
15
- name: string
16
- display_name: string
17
- youth: boolean
18
- }
19
- iss: string
20
- }
21
-
22
- export type RefreshToken = {
23
- jti: string
24
- ati: string
25
- iat: number
26
- aud: string
27
- sub: string
28
- exp: number // exp is in seconds
29
- scopes: Scope[]
30
- }
31
-
32
- export type AccessToken = RefreshToken
33
-
34
- export type RawTokens = { idToken: string; refreshToken: string; accessToken: string }
35
-
36
- export type Scope =
37
- | 'user.basic.read'
38
- | 'user.establishment.read'
39
- | 'user.booking.read'
40
- | 'user.clients.read'
41
- | 'user.clients.delete'
42
-
43
- export const tokenFromJwt = <T>(token: string): T => JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString())
44
-
45
- export const oauthClientToken = (clientId: string, clientSecret: string): string => {
46
- const token = Buffer.from(`${clientId}:${clientSecret}`).toString('base64')
47
- return `Basic ${token}`
48
- }
49
-
50
- export type TokenFetcher = (refreshToken: string, tokenUrl: string, authorizationHeader: string) => Promise<RawTokens>
51
-
52
- export const tokenFetcher: TokenFetcher = (userRefreshToken, tokenUrl, authorizationHeader) => {
53
- return new Promise<RawTokens>((resolve, reject) => {
54
- superagent
55
- .post(tokenUrl)
56
- .set('Content-Type', 'application/x-www-form-urlencoded')
57
- .set('Authorization', authorizationHeader)
58
- .query({ refresh_token: userRefreshToken, grant_type: 'refresh_token' })
59
- .end((err, res) => {
60
- if (err) reject(err)
61
- const { id_token: idToken, access_token: accessToken, refresh_token: refreshToken } = res.body
62
- return resolve({ idToken, accessToken, refreshToken })
63
- })
64
- })
65
- }
@@ -1,154 +0,0 @@
1
- diff --git a/server/app.ts b/server/app.ts
2
- index eddaa67..26fb35e 100644
3
- --- a/server/app.ts
4
- +++ b/server/app.ts
5
- @@ -5,11 +5,9 @@ import createError from 'http-errors'
6
- import nunjucksSetup from './utils/nunjucksSetup'
7
- import errorHandler from './errorHandler'
8
- import { appInsightsMiddleware } from './utils/azureAppInsights'
9
- -import authorisationMiddleware from './middleware/authorisationMiddleware'
10
-
11
- import setUpAuthentication from './middleware/setUpAuthentication'
12
- import setUpCsrf from './middleware/setUpCsrf'
13
- -import setUpCurrentUser from './middleware/setUpCurrentUser'
14
- import setUpHealthChecks from './middleware/setUpHealthChecks'
15
- import setUpStaticResources from './middleware/setUpStaticResources'
16
- import setUpWebRequestParsing from './middleware/setupRequestParsing'
17
- @@ -34,9 +32,7 @@ export default function createApp(services: Services): express.Application {
18
- app.use(setUpStaticResources())
19
- nunjucksSetup(app)
20
- app.use(setUpAuthentication())
21
- - app.use(authorisationMiddleware())
22
- app.use(setUpCsrf())
23
- - app.use(setUpCurrentUser())
24
-
25
- app.use(routes(services))
26
-
27
- diff --git a/server/interfaces/hmppsUser.ts b/server/interfaces/hmppsUser.ts
28
- index f0a6dd2..8c01550 100644
29
- --- a/server/interfaces/hmppsUser.ts
30
- +++ b/server/interfaces/hmppsUser.ts
31
- @@ -1,4 +1,6 @@
32
- -export type AuthSource = 'nomis' | 'delius' | 'external' | 'azuread'
33
- +import { LaunchpadUser } from '@ministryofjustice/hmpps-prisoner-auth'
34
- +
35
- +export type AuthSource = 'nomis' | 'delius' | 'external' | 'azuread' | 'prisoner-auth'
36
-
37
- /**
38
- * These are the details that all user types share.
39
- @@ -57,4 +59,4 @@ export interface AzureADUser extends BaseUser {
40
- authSource: 'azuread'
41
- }
42
-
43
- -export type HmppsUser = PrisonUser | ProbationUser | ExternalUser | AzureADUser
44
- +export type HmppsUser = PrisonUser | ProbationUser | ExternalUser | AzureADUser | LaunchpadUser
45
- diff --git a/server/middleware/setUpAuthentication.ts b/server/middleware/setUpAuthentication.ts
46
- index 1979d6e..a954198 100644
47
- --- a/server/middleware/setUpAuthentication.ts
48
- +++ b/server/middleware/setUpAuthentication.ts
49
- @@ -1,12 +1,8 @@
50
- import passport from 'passport'
51
- import flash from 'connect-flash'
52
- import { Router } from 'express'
53
- -import { Strategy } from 'passport-oauth2'
54
- -import { VerificationClient, AuthenticatedRequest } from '@ministryofjustice/hmpps-auth-clients'
55
- -import config from '../config'
56
- +import { LaunchpadUser, PrisonerAuth, minutes } from '@ministryofjustice/hmpps-prisoner-auth'
57
- import { HmppsUser } from '../interfaces/hmppsUser'
58
- -import generateOauthClientToken from '../utils/clientCredentials'
59
- -import logger from '../../logger'
60
-
61
- passport.serializeUser((user, done) => {
62
- // Not used but required for Passport
63
- @@ -18,26 +14,17 @@ passport.deserializeUser((user, done) => {
64
- done(null, user as Express.User)
65
- })
66
-
67
- -passport.use(
68
- - new Strategy(
69
- - {
70
- - authorizationURL: `${config.apis.hmppsAuth.externalUrl}/oauth/authorize`,
71
- - tokenURL: `${config.apis.hmppsAuth.url}/oauth/token`,
72
- - clientID: config.apis.hmppsAuth.authClientId,
73
- - clientSecret: config.apis.hmppsAuth.authClientSecret,
74
- - callbackURL: `${config.ingressUrl}/sign-in/callback`,
75
- - state: true,
76
- - customHeaders: { Authorization: generateOauthClientToken() },
77
- - },
78
- - (token, refreshToken, params, profile, done) => {
79
- - return done(null, { token, username: params.user_name, authSource: params.auth_source })
80
- - },
81
- - ),
82
- -)
83
- +const prisonerAuth = new PrisonerAuth({
84
- + launchpadAuthUrl: 'VALUE_HERE',
85
- + clientId: 'VALUE_HERE',
86
- + clientSecret: 'VALUE_HERE',
87
- + tokenMinimumLifespan: minutes(5),
88
- +})
89
- +
90
- +passport.use('prisoner-auth', prisonerAuth.passportStrategy())
91
-
92
- export default function setupAuthentication() {
93
- const router = Router()
94
- - const tokenVerificationClient = new VerificationClient(config.apis.tokenVerification, logger)
95
-
96
- router.use(passport.initialize())
97
- router.use(passport.session())
98
- @@ -48,40 +35,39 @@ export default function setupAuthentication() {
99
- return res.render('autherror')
100
- })
101
-
102
- - router.get('/sign-in', passport.authenticate('oauth2'))
103
- + router.get('/sign-in', passport.authenticate('prisoner-auth'))
104
-
105
- router.get('/sign-in/callback', (req, res, next) =>
106
- - passport.authenticate('oauth2', {
107
- + passport.authenticate('prisoner-auth', {
108
- successReturnToOrRedirect: req.session.returnTo || '/',
109
- failureRedirect: '/autherror',
110
- })(req, res, next),
111
- )
112
-
113
- - const authUrl = config.apis.hmppsAuth.externalUrl
114
- - const authParameters = `client_id=${config.apis.hmppsAuth.authClientId}&redirect_uri=${config.ingressUrl}`
115
- -
116
- router.use('/sign-out', (req, res, next) => {
117
- - const authSignOutUrl = `${authUrl}/sign-out?${authParameters}`
118
- if (req.user) {
119
- req.logout(err => {
120
- if (err) return next(err)
121
- - return req.session.destroy(() => res.redirect(authSignOutUrl))
122
- + return req.session.destroy(() => res.redirect('/'))
123
- })
124
- - } else res.redirect(authSignOutUrl)
125
- - })
126
- -
127
- - router.use('/account-details', (req, res) => {
128
- - res.redirect(`${authUrl}/account-details?${authParameters}`)
129
- + } else res.redirect('/')
130
- })
131
-
132
- router.use(async (req, res, next) => {
133
- - if (req.isAuthenticated() && (await tokenVerificationClient.verifyToken(req as unknown as AuthenticatedRequest))) {
134
- - return next()
135
- + if (!req.isAuthenticated()) {
136
- + req.session.returnTo = req.originalUrl
137
- + return res.redirect('/sign-in')
138
- }
139
- - req.session.returnTo = req.originalUrl
140
- - return res.redirect('/sign-in')
141
- - })
142
-
143
- + return prisonerAuth
144
- + .validateAndRefreshUser(req.user as LaunchpadUser)
145
- + .then(user => {
146
- + req.user = user
147
- + next()
148
- + })
149
- + .catch(() => res.redirect('/autherror'))
150
- + })
151
- +
152
- router.use((req, res, next) => {
153
- res.locals.user = req.user as HmppsUser
154
- next()