@sphereon/ssi-express-support 0.30.1-unstable.4 → 0.30.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.
@@ -1,49 +1,49 @@
1
- import express, { NextFunction } from 'express'
2
- export function sendErrorResponse(response: express.Response, statusCode: number, message: string | object, error?: any) {
3
- let msg = message
4
- if (!msg) {
5
- console.error('Message was null when calling sendErrorResponse. This should not happen')
6
- msg = 'An unexpected error occurred'
7
- statusCode = 500
8
- } else {
9
- console.error(`sendErrorResponse (${statusCode}): ${typeof msg === 'string' ? msg : JSON.stringify(msg)}`)
10
- }
11
- if (error) {
12
- if (error instanceof Error) {
13
- console.error(`error message: ${error.message}`)
14
- }
15
- console.error(`error object: ${JSON.stringify(error)}`)
16
- }
17
- if (statusCode >= 500) {
18
- console.error('Original error stack (if any) and REST API error stack:')
19
- console.error(error?.stack)
20
- console.error(Error().stack)
21
- }
22
- if (response.headersSent) {
23
- console.error(`sendErrorResponse headers already sent`)
24
- return response
25
- }
26
- response.statusCode = statusCode
27
- if (typeof msg === 'string' && !msg.startsWith('{')) {
28
- msg = { error: msg }
29
- }
30
- if (typeof msg === 'string' && msg.startsWith('{')) {
31
- response.header('Content-Type', 'application/json')
32
- return response.status(statusCode).end(msg)
33
- }
34
- return response.status(statusCode).json(msg)
35
- }
36
-
37
- export const jsonErrorHandler = (err: any, req: express.Request, res: express.Response, next: NextFunction) => {
38
- const statusCode: number = 'statusCode' in err ? err.statusCode : 500
39
- let errorMsg = typeof err === 'string' ? err : (err.message ?? err)
40
- if (typeof errorMsg !== 'string') {
41
- errorMsg = JSON.stringify(errorMsg)
42
- }
43
- if (res.headersSent) {
44
- console.log('Headers already sent, when calling error handler. Will defer to next error handler')
45
- console.log(`Error was: ${JSON.stringify(err)}`)
46
- return next(err)
47
- }
48
- return sendErrorResponse(res, statusCode, errorMsg, err)
49
- }
1
+ import express, { NextFunction } from 'express'
2
+ export function sendErrorResponse(response: express.Response, statusCode: number, message: string | object, error?: any) {
3
+ let msg = message
4
+ if (!msg) {
5
+ console.error('Message was null when calling sendErrorResponse. This should not happen')
6
+ msg = 'An unexpected error occurred'
7
+ statusCode = 500
8
+ } else {
9
+ console.error(`sendErrorResponse (${statusCode}): ${typeof msg === 'string' ? msg : JSON.stringify(msg)}`)
10
+ }
11
+ if (error) {
12
+ if (error instanceof Error) {
13
+ console.error(`error message: ${error.message}`)
14
+ }
15
+ console.error(`error object: ${JSON.stringify(error)}`)
16
+ }
17
+ if (statusCode >= 500) {
18
+ console.error('Original error stack (if any) and REST API error stack:')
19
+ console.error(error?.stack)
20
+ console.error(Error().stack)
21
+ }
22
+ if (response.headersSent) {
23
+ console.error(`sendErrorResponse headers already sent`)
24
+ return response
25
+ }
26
+ response.statusCode = statusCode
27
+ if (typeof msg === 'string' && !msg.startsWith('{')) {
28
+ msg = { error: msg }
29
+ }
30
+ if (typeof msg === 'string' && msg.startsWith('{')) {
31
+ response.header('Content-Type', 'application/json')
32
+ return response.status(statusCode).end(msg)
33
+ }
34
+ return response.status(statusCode).json(msg)
35
+ }
36
+
37
+ export const jsonErrorHandler = (err: any, req: express.Request, res: express.Response, next: NextFunction) => {
38
+ const statusCode: number = 'statusCode' in err ? err.statusCode : 500
39
+ let errorMsg = typeof err === 'string' ? err : (err.message ?? err)
40
+ if (typeof errorMsg !== 'string') {
41
+ errorMsg = JSON.stringify(errorMsg)
42
+ }
43
+ if (res.headersSent) {
44
+ console.log('Headers already sent, when calling error handler. Will defer to next error handler')
45
+ console.log(`Error was: ${JSON.stringify(err)}`)
46
+ return next(err)
47
+ }
48
+ return sendErrorResponse(res, statusCode, errorMsg, err)
49
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
- export * from './entra-id-auth'
2
- export * from './static-bearer-auth'
3
- export * from './auth-utils'
4
- export * from './express-builders'
5
- export * from './types'
6
- export { sendErrorResponse, jsonErrorHandler } from './express-utils'
7
- export * from './functions'
8
- export * from './openid-connect-rp'
1
+ export * from './entra-id-auth'
2
+ export * from './static-bearer-auth'
3
+ export * from './auth-utils'
4
+ export * from './express-builders'
5
+ export * from './types'
6
+ export { sendErrorResponse, jsonErrorHandler } from './express-utils'
7
+ export * from './functions'
8
+ export * from './openid-connect-rp'
@@ -1,228 +1,228 @@
1
- import { TAgent } from '@veramo/core'
2
- import express, { Express, NextFunction, Router } from 'express'
3
- import { BaseClient, ClientMetadata, ClientOptions, Issuer } from 'openid-client'
4
- import passport from 'passport'
5
- import { copyGlobalAuthToEndpoints, isUserAuthenticated } from './auth-utils'
6
- import { sendErrorResponse } from './express-utils'
7
- import { env } from './functions'
8
- import { ExpressSupport, GenericAuthArgs, ISingleEndpointOpts } from './types'
9
-
10
- const PREFIX = process.env.PREFIX ?? ''
11
- export async function oidcDiscoverIssuer(opts?: { issuerUrl?: string }) {
12
- const issuerUrl = opts?.issuerUrl ?? env('OIDC_ISSUER', PREFIX) ?? 'https://auth01.test.sphereon.com/auth/realms/energy-shr'
13
- const issuer = await Issuer.discover(issuerUrl)
14
- console.log('Discovered issuer %s %O', issuer.issuer, issuer.metadata)
15
- return { issuer, issuerUrl }
16
- }
17
-
18
- export async function oidcGetClient(
19
- issuer: Issuer<BaseClient>,
20
- metadata: ClientMetadata,
21
- opts?: {
22
- jwks?: { keys: JsonWebKey[] }
23
- options?: ClientOptions
24
- },
25
- ) {
26
- // @ts-ignore
27
- return new issuer.Client(metadata, opts?.jwks, opts?.options)
28
- }
29
-
30
- export function getLoginEndpoint(router: Router, opts?: ISingleEndpointOpts & { redirectUrl?: string }) {
31
- if (opts?.enabled === false) {
32
- console.log(`Login endpoint is disabled`)
33
- return
34
- }
35
- const strategy = opts?.endpoint?.authentication?.strategy
36
- if (!strategy) {
37
- throw Error('strategy needs to be provided')
38
- }
39
- const path = opts?.path ?? '/authentication/login'
40
- router.get(
41
- path,
42
- (req: any, res: any, next: NextFunction) => {
43
- const redirectPage = req.get('referer') ?? '/'
44
- req.session.redirectPage = redirectPage
45
- next()
46
- },
47
- passport.authenticate(
48
- strategy,
49
- { ...opts.authentication?.strategyOptions, ...opts.endpoint?.authentication?.strategyOptions, keepSessionInfo: false },
50
- undefined,
51
- ),
52
- )
53
- }
54
-
55
- export function getLoginCallbackEndpoint(router: Router, opts?: ISingleEndpointOpts) {
56
- if (opts?.enabled === false) {
57
- console.log(`Auth callback endpoint is disabled`)
58
- return
59
- }
60
- const strategy = opts?.endpoint?.authentication?.strategy
61
- if (!strategy) {
62
- throw Error('strategy needs to be provided')
63
- }
64
- const path = opts?.path ?? '/authentication/callback'
65
- router.get(
66
- path,
67
- passport.authenticate(
68
- strategy,
69
- { ...opts.authentication?.strategyOptions, ...opts.endpoint?.authentication?.strategyOptions, keepSessionInfo: true },
70
- undefined,
71
- ),
72
- (req: any, res: any, next) => {
73
- if (req.user) {
74
- console.log('User authenticated', req.user?.name)
75
- // console.log(req.session)
76
- const redirectPage = req.session.redirectPage ?? '/search'
77
- // console.log(`PRE LOGIN PAGE in callback: ${redirectPage}`)
78
- delete req.session.redirectPage
79
- return res.redirect(redirectPage)
80
- } else {
81
- return res.redirect(env('OIDC_FRONTEND_LOGIN_URL', PREFIX) ?? 'http://localhost:3001/authentication/login')
82
- }
83
- },
84
- )
85
- }
86
-
87
- export function getLogoutEndpoint(router: Router, client: BaseClient, opts?: ISingleEndpointOpts) {
88
- if (opts?.enabled === false) {
89
- console.log(`Logout endpoint is disabled`)
90
- return
91
- }
92
- const path = opts?.path ?? '/authentication/logout'
93
- router.get(path, (req, res) => {
94
- try {
95
- if (client.endSessionUrl()) {
96
- return res.redirect(client.endSessionUrl())
97
- } else {
98
- console.log('IDP does not support end session url')
99
- return res.redirect('/authentication/logout-callback')
100
- }
101
- } catch (error) {
102
- console.log(error)
103
- return res.redirect('/authentication/logout-callback')
104
- }
105
- })
106
- }
107
-
108
- export function getLogoutCallbackEndpoint(router: Router, opts?: ISingleEndpointOpts) {
109
- if (opts?.enabled === false) {
110
- console.log(`Logout callback endpoint is disabled`)
111
- return
112
- }
113
- const path = opts?.path ?? '/authentication/logout-callback'
114
- router.get(path, (req, res, next) => {
115
- try {
116
- req.logout((err) => {
117
- if (err) {
118
- console.log(`Error during calling logout-callback: ${JSON.stringify(err)}`)
119
- }
120
- })
121
- return res.redirect(env('OIDC_FRONTEND_LOGOUT_REDIRECT_URL', PREFIX) ?? '/')
122
- } catch (e) {
123
- return sendErrorResponse(res, 500, 'An unexpected error occurred during logout callback', e)
124
- }
125
- })
126
- }
127
-
128
- export function getIdTokenEndpoint(router: Router, client: BaseClient, opts: ISingleEndpointOpts) {
129
- if (opts?.enabled === false) {
130
- console.log(`ID Token endpoint is disabled`)
131
- return
132
- }
133
- const path = opts.path ?? '/authentication/tokens/id'
134
- router.get(path, isUserAuthenticated, (req: any, res: any) => {
135
- if (req.session.tokens.id_token) {
136
- return res.json({ id_token: req.session.tokens.id_token })
137
- } else {
138
- return sendErrorResponse(res, 401, 'Authentication required')
139
- }
140
- })
141
- }
142
-
143
- export function getAuthenticatedUserEndpoint(router: Router, opts?: ISingleEndpointOpts) {
144
- if (opts?.enabled === false) {
145
- console.log(`Authenticated User endpoint is disabled`)
146
- return
147
- }
148
- const path = opts?.path ?? '/authentication/user'
149
- router.get(path, isUserAuthenticated, (req: any, res: any, next: any) => {
150
- if (!req.user) {
151
- return sendErrorResponse(res, 401, 'Authentication required')
152
- }
153
- let user = req.user
154
- return res.json(user)
155
- })
156
- }
157
-
158
- export interface IAuthenticationOpts {
159
- enabledFeatures?: AuthenticationApiFeatures
160
- endpointOpts?: IAuthenticationEndpointOpts
161
- }
162
-
163
- export interface IAuthenticationEndpointOpts {
164
- basePath?: string
165
- globalAuth?: GenericAuthArgs
166
- getAuthenticatedUser?: ISingleEndpointOpts
167
- getLogin?: ISingleEndpointOpts
168
- getLogout?: ISingleEndpointOpts
169
- getIdToken?: ISingleEndpointOpts
170
- }
171
-
172
- export type AuthenticationApiFeatures = 'login' | 'logout' | 'id-token' | 'authenticated-user'
173
-
174
- export class OpenIDConnectAuthApi {
175
- get router(): express.Router {
176
- return this._router
177
- }
178
-
179
- private readonly _express: Express
180
- private readonly _agent?: TAgent<any>
181
- private readonly _opts?: IAuthenticationOpts
182
- private readonly _router: Router
183
-
184
- constructor(args: { agent?: TAgent<any>; expressSupport: ExpressSupport; client: BaseClient; opts: IAuthenticationOpts }) {
185
- const { agent, opts } = args
186
- this._agent = agent
187
- copyGlobalAuthToEndpoints({ opts, keys: ['getLogin'] })
188
- copyGlobalAuthToEndpoints({ opts, keys: ['getIdToken'] })
189
- copyGlobalAuthToEndpoints({ opts, keys: ['getAuthenticatedUser'] })
190
- // no need for the logout, as you these are not protected by auth
191
- this._opts = opts
192
- this._express = args.expressSupport.express
193
- this._router = express.Router()
194
- const features = opts?.enabledFeatures ?? ['login', 'logout', 'id-token', 'authenticated-user']
195
- console.log(`Authentication API enabled`)
196
-
197
- if (features.includes('login')) {
198
- getLoginEndpoint(this.router, opts?.endpointOpts?.getLogin)
199
- getLoginCallbackEndpoint(this.router, opts?.endpointOpts?.getLogin)
200
- }
201
- if (features.includes('logout')) {
202
- getLogoutEndpoint(this.router, args.client, opts?.endpointOpts?.getLogout)
203
- getLogoutCallbackEndpoint(this.router, opts?.endpointOpts?.getLogout)
204
- }
205
- if (features.includes('id-token')) {
206
- if (opts.endpointOpts?.getIdToken === undefined) {
207
- throw Error('Cannot enable id-token endpoint without providing id-token endpoint options')
208
- }
209
- getIdTokenEndpoint(this.router, args.client, opts?.endpointOpts?.getIdToken)
210
- }
211
- if (features.includes('authenticated-user')) {
212
- getAuthenticatedUserEndpoint(this.router, opts?.endpointOpts?.getAuthenticatedUser)
213
- }
214
- this._express.use(opts?.endpointOpts?.basePath ?? '', this.router)
215
- }
216
-
217
- get agent(): TAgent<any> | undefined {
218
- return this._agent
219
- }
220
-
221
- get opts(): IAuthenticationOpts | undefined {
222
- return this._opts
223
- }
224
-
225
- get express(): Express {
226
- return this._express
227
- }
228
- }
1
+ import { TAgent } from '@veramo/core'
2
+ import express, { Express, NextFunction, Router } from 'express'
3
+ import { BaseClient, ClientMetadata, ClientOptions, Issuer } from 'openid-client'
4
+ import passport from 'passport'
5
+ import { copyGlobalAuthToEndpoints, isUserAuthenticated } from './auth-utils'
6
+ import { sendErrorResponse } from './express-utils'
7
+ import { env } from './functions'
8
+ import { ExpressSupport, GenericAuthArgs, ISingleEndpointOpts } from './types'
9
+
10
+ const PREFIX = process.env.PREFIX ?? ''
11
+ export async function oidcDiscoverIssuer(opts?: { issuerUrl?: string }) {
12
+ const issuerUrl = opts?.issuerUrl ?? env('OIDC_ISSUER', PREFIX) ?? 'https://auth01.test.sphereon.com/auth/realms/energy-shr'
13
+ const issuer = await Issuer.discover(issuerUrl)
14
+ console.log('Discovered issuer %s %O', issuer.issuer, issuer.metadata)
15
+ return { issuer, issuerUrl }
16
+ }
17
+
18
+ export async function oidcGetClient(
19
+ issuer: Issuer<BaseClient>,
20
+ metadata: ClientMetadata,
21
+ opts?: {
22
+ jwks?: { keys: JsonWebKey[] }
23
+ options?: ClientOptions
24
+ },
25
+ ) {
26
+ // @ts-ignore
27
+ return new issuer.Client(metadata, opts?.jwks, opts?.options)
28
+ }
29
+
30
+ export function getLoginEndpoint(router: Router, opts?: ISingleEndpointOpts & { redirectUrl?: string }) {
31
+ if (opts?.enabled === false) {
32
+ console.log(`Login endpoint is disabled`)
33
+ return
34
+ }
35
+ const strategy = opts?.endpoint?.authentication?.strategy
36
+ if (!strategy) {
37
+ throw Error('strategy needs to be provided')
38
+ }
39
+ const path = opts?.path ?? '/authentication/login'
40
+ router.get(
41
+ path,
42
+ (req: any, res: any, next: NextFunction) => {
43
+ const redirectPage = req.get('referer') ?? '/'
44
+ req.session.redirectPage = redirectPage
45
+ next()
46
+ },
47
+ passport.authenticate(
48
+ strategy,
49
+ { ...opts.authentication?.strategyOptions, ...opts.endpoint?.authentication?.strategyOptions, keepSessionInfo: false },
50
+ undefined,
51
+ ),
52
+ )
53
+ }
54
+
55
+ export function getLoginCallbackEndpoint(router: Router, opts?: ISingleEndpointOpts) {
56
+ if (opts?.enabled === false) {
57
+ console.log(`Auth callback endpoint is disabled`)
58
+ return
59
+ }
60
+ const strategy = opts?.endpoint?.authentication?.strategy
61
+ if (!strategy) {
62
+ throw Error('strategy needs to be provided')
63
+ }
64
+ const path = opts?.path ?? '/authentication/callback'
65
+ router.get(
66
+ path,
67
+ passport.authenticate(
68
+ strategy,
69
+ { ...opts.authentication?.strategyOptions, ...opts.endpoint?.authentication?.strategyOptions, keepSessionInfo: true },
70
+ undefined,
71
+ ),
72
+ (req: any, res: any, next) => {
73
+ if (req.user) {
74
+ console.log('User authenticated', req.user?.name)
75
+ // console.log(req.session)
76
+ const redirectPage = req.session.redirectPage ?? '/search'
77
+ // console.log(`PRE LOGIN PAGE in callback: ${redirectPage}`)
78
+ delete req.session.redirectPage
79
+ return res.redirect(redirectPage)
80
+ } else {
81
+ return res.redirect(env('OIDC_FRONTEND_LOGIN_URL', PREFIX) ?? 'http://localhost:3001/authentication/login')
82
+ }
83
+ },
84
+ )
85
+ }
86
+
87
+ export function getLogoutEndpoint(router: Router, client: BaseClient, opts?: ISingleEndpointOpts) {
88
+ if (opts?.enabled === false) {
89
+ console.log(`Logout endpoint is disabled`)
90
+ return
91
+ }
92
+ const path = opts?.path ?? '/authentication/logout'
93
+ router.get(path, (req, res) => {
94
+ try {
95
+ if (client.endSessionUrl()) {
96
+ return res.redirect(client.endSessionUrl())
97
+ } else {
98
+ console.log('IDP does not support end session url')
99
+ return res.redirect('/authentication/logout-callback')
100
+ }
101
+ } catch (error) {
102
+ console.log(error)
103
+ return res.redirect('/authentication/logout-callback')
104
+ }
105
+ })
106
+ }
107
+
108
+ export function getLogoutCallbackEndpoint(router: Router, opts?: ISingleEndpointOpts) {
109
+ if (opts?.enabled === false) {
110
+ console.log(`Logout callback endpoint is disabled`)
111
+ return
112
+ }
113
+ const path = opts?.path ?? '/authentication/logout-callback'
114
+ router.get(path, (req, res, next) => {
115
+ try {
116
+ req.logout((err) => {
117
+ if (err) {
118
+ console.log(`Error during calling logout-callback: ${JSON.stringify(err)}`)
119
+ }
120
+ })
121
+ return res.redirect(env('OIDC_FRONTEND_LOGOUT_REDIRECT_URL', PREFIX) ?? '/')
122
+ } catch (e) {
123
+ return sendErrorResponse(res, 500, 'An unexpected error occurred during logout callback', e)
124
+ }
125
+ })
126
+ }
127
+
128
+ export function getIdTokenEndpoint(router: Router, client: BaseClient, opts: ISingleEndpointOpts) {
129
+ if (opts?.enabled === false) {
130
+ console.log(`ID Token endpoint is disabled`)
131
+ return
132
+ }
133
+ const path = opts.path ?? '/authentication/tokens/id'
134
+ router.get(path, isUserAuthenticated, (req: any, res: any) => {
135
+ if (req.session.tokens.id_token) {
136
+ return res.json({ id_token: req.session.tokens.id_token })
137
+ } else {
138
+ return sendErrorResponse(res, 401, 'Authentication required')
139
+ }
140
+ })
141
+ }
142
+
143
+ export function getAuthenticatedUserEndpoint(router: Router, opts?: ISingleEndpointOpts) {
144
+ if (opts?.enabled === false) {
145
+ console.log(`Authenticated User endpoint is disabled`)
146
+ return
147
+ }
148
+ const path = opts?.path ?? '/authentication/user'
149
+ router.get(path, isUserAuthenticated, (req: any, res: any, next: any) => {
150
+ if (!req.user) {
151
+ return sendErrorResponse(res, 401, 'Authentication required')
152
+ }
153
+ let user = req.user
154
+ return res.json(user)
155
+ })
156
+ }
157
+
158
+ export interface IAuthenticationOpts {
159
+ enabledFeatures?: AuthenticationApiFeatures
160
+ endpointOpts?: IAuthenticationEndpointOpts
161
+ }
162
+
163
+ export interface IAuthenticationEndpointOpts {
164
+ basePath?: string
165
+ globalAuth?: GenericAuthArgs
166
+ getAuthenticatedUser?: ISingleEndpointOpts
167
+ getLogin?: ISingleEndpointOpts
168
+ getLogout?: ISingleEndpointOpts
169
+ getIdToken?: ISingleEndpointOpts
170
+ }
171
+
172
+ export type AuthenticationApiFeatures = 'login' | 'logout' | 'id-token' | 'authenticated-user'
173
+
174
+ export class OpenIDConnectAuthApi {
175
+ get router(): express.Router {
176
+ return this._router
177
+ }
178
+
179
+ private readonly _express: Express
180
+ private readonly _agent?: TAgent<any>
181
+ private readonly _opts?: IAuthenticationOpts
182
+ private readonly _router: Router
183
+
184
+ constructor(args: { agent?: TAgent<any>; expressSupport: ExpressSupport; client: BaseClient; opts: IAuthenticationOpts }) {
185
+ const { agent, opts } = args
186
+ this._agent = agent
187
+ copyGlobalAuthToEndpoints({ opts, keys: ['getLogin'] })
188
+ copyGlobalAuthToEndpoints({ opts, keys: ['getIdToken'] })
189
+ copyGlobalAuthToEndpoints({ opts, keys: ['getAuthenticatedUser'] })
190
+ // no need for the logout, as you these are not protected by auth
191
+ this._opts = opts
192
+ this._express = args.expressSupport.express
193
+ this._router = express.Router()
194
+ const features = opts?.enabledFeatures ?? ['login', 'logout', 'id-token', 'authenticated-user']
195
+ console.log(`Authentication API enabled`)
196
+
197
+ if (features.includes('login')) {
198
+ getLoginEndpoint(this.router, opts?.endpointOpts?.getLogin)
199
+ getLoginCallbackEndpoint(this.router, opts?.endpointOpts?.getLogin)
200
+ }
201
+ if (features.includes('logout')) {
202
+ getLogoutEndpoint(this.router, args.client, opts?.endpointOpts?.getLogout)
203
+ getLogoutCallbackEndpoint(this.router, opts?.endpointOpts?.getLogout)
204
+ }
205
+ if (features.includes('id-token')) {
206
+ if (opts.endpointOpts?.getIdToken === undefined) {
207
+ throw Error('Cannot enable id-token endpoint without providing id-token endpoint options')
208
+ }
209
+ getIdTokenEndpoint(this.router, args.client, opts?.endpointOpts?.getIdToken)
210
+ }
211
+ if (features.includes('authenticated-user')) {
212
+ getAuthenticatedUserEndpoint(this.router, opts?.endpointOpts?.getAuthenticatedUser)
213
+ }
214
+ this._express.use(opts?.endpointOpts?.basePath ?? '', this.router)
215
+ }
216
+
217
+ get agent(): TAgent<any> | undefined {
218
+ return this._agent
219
+ }
220
+
221
+ get opts(): IAuthenticationOpts | undefined {
222
+ return this._opts
223
+ }
224
+
225
+ get express(): Express {
226
+ return this._express
227
+ }
228
+ }