@sphereon/ssi-express-support 0.37.1-next.5 → 0.37.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/dist/index.cjs +128 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +128 -10
- package/dist/index.js.map +1 -1
- package/package.json +15 -4
- package/src/index.ts +1 -0
- package/src/oidc-bearer-auth.ts +148 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphereon/ssi-express-support",
|
|
3
|
-
"version": "0.37.1
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"source": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"start:energyshr": "cross-env ENVIRONMENT=EnergySHR NODE_ENV=energyshr node --experimental-specifier-resolution=node --loader ts-node/esm __tests__/index.ts"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@sphereon/ssi-types": "0.37.1
|
|
26
|
+
"@sphereon/ssi-types": "0.37.1",
|
|
27
27
|
"body-parser": "^1.20.2",
|
|
28
28
|
"casbin": "^5.30.0",
|
|
29
29
|
"cookie-session": "^2.1.0",
|
|
@@ -51,15 +51,20 @@
|
|
|
51
51
|
"@types/passport": "^1.0.16",
|
|
52
52
|
"@types/passport-azure-ad": "^4.3.6",
|
|
53
53
|
"@types/passport-http-bearer": "^1.0.41",
|
|
54
|
+
"@types/passport-jwt": "^4.0.1",
|
|
54
55
|
"@types/qs": "^6.9.15",
|
|
55
56
|
"cross-env": "^7.0.3",
|
|
56
57
|
"jose": "^4.15.9",
|
|
58
|
+
"jwks-rsa": "^3.2.2",
|
|
59
|
+
"passport-jwt": "^4.0.1",
|
|
57
60
|
"typescript": "5.8.3"
|
|
58
61
|
},
|
|
59
62
|
"peerDependencies": {
|
|
60
63
|
"@noble/hashes": "1.6.1",
|
|
64
|
+
"jwks-rsa": "^3.2.2",
|
|
61
65
|
"passport-azure-ad": "^4.3.5",
|
|
62
|
-
"passport-http-bearer": "^1.0.1"
|
|
66
|
+
"passport-http-bearer": "^1.0.1",
|
|
67
|
+
"passport-jwt": "^4.0.1"
|
|
63
68
|
},
|
|
64
69
|
"peerDependenciesMeta": {
|
|
65
70
|
"passport-http-bearer": {
|
|
@@ -70,6 +75,12 @@
|
|
|
70
75
|
},
|
|
71
76
|
"passport-azure-ad": {
|
|
72
77
|
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"jwks-rsa": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"passport-jwt": {
|
|
83
|
+
"optional": true
|
|
73
84
|
}
|
|
74
85
|
},
|
|
75
86
|
"files": [
|
|
@@ -89,5 +100,5 @@
|
|
|
89
100
|
"SSI",
|
|
90
101
|
"Agent"
|
|
91
102
|
],
|
|
92
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "f77778193dc9235727d306be0449c5bf05b63cbe"
|
|
93
104
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import passport from 'passport'
|
|
2
|
+
|
|
3
|
+
export type OIDCAlgorithm = 'RS256' | 'RS384' | 'RS512' | 'ES256' | 'ES384' | 'ES512' | 'PS256' | 'PS384' | 'PS512'
|
|
4
|
+
|
|
5
|
+
export interface IOIDCBearerOptions {
|
|
6
|
+
issuer: string
|
|
7
|
+
audience?: string | string[]
|
|
8
|
+
jwksUri?: string
|
|
9
|
+
algorithms?: OIDCAlgorithm[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IOIDCTokenPayload {
|
|
13
|
+
/** Issuer identifier */
|
|
14
|
+
iss?: string
|
|
15
|
+
/** Subject identifier */
|
|
16
|
+
sub?: string
|
|
17
|
+
/** Audience(s) */
|
|
18
|
+
aud?: string | string[]
|
|
19
|
+
/** Expiration time */
|
|
20
|
+
exp?: number
|
|
21
|
+
/** Not before */
|
|
22
|
+
nbf?: number
|
|
23
|
+
/** Issued at */
|
|
24
|
+
iat?: number
|
|
25
|
+
/** JWT ID */
|
|
26
|
+
jti?: string
|
|
27
|
+
/** Authorized party */
|
|
28
|
+
azp?: string
|
|
29
|
+
/** Scope */
|
|
30
|
+
scope?: string
|
|
31
|
+
/** Client ID */
|
|
32
|
+
client_id?: string
|
|
33
|
+
/** Additional claims */
|
|
34
|
+
[key: string]: unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class OIDCBearerAuth {
|
|
38
|
+
private readonly strategy: string
|
|
39
|
+
private options: Partial<IOIDCBearerOptions> = {}
|
|
40
|
+
|
|
41
|
+
public static init(strategy: string) {
|
|
42
|
+
return new OIDCBearerAuth(strategy)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private constructor(strategy: string) {
|
|
46
|
+
this.strategy = strategy
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public withIssuer(issuer: string): this {
|
|
50
|
+
this.options = { ...this.options, issuer }
|
|
51
|
+
return this
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public withAudience(audience: string | string[]): this {
|
|
55
|
+
this.options = { ...this.options, audience }
|
|
56
|
+
return this
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public withJwksUri(jwksUri: string): this {
|
|
60
|
+
this.options = { ...this.options, jwksUri }
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public withAlgorithms(algorithms: OIDCAlgorithm[]): this {
|
|
65
|
+
this.options = { ...this.options, algorithms }
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public withOptions(options: Partial<IOIDCBearerOptions>): this {
|
|
70
|
+
this.options = { ...this.options, ...options }
|
|
71
|
+
return this
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async connectPassport(): Promise<void> {
|
|
75
|
+
const { issuer, audience, algorithms } = this.options
|
|
76
|
+
|
|
77
|
+
if (!issuer) {
|
|
78
|
+
return Promise.reject(new Error('No issuer supplied for OIDC Bearer Auth'))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let jwksUri = this.options.jwksUri
|
|
82
|
+
if (!jwksUri) {
|
|
83
|
+
jwksUri = await this.discoverJwksUri(issuer)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const [passportJwt, jwksRsa] = await Promise.all([import('passport-jwt'), import('jwks-rsa')])
|
|
88
|
+
|
|
89
|
+
const { Strategy: JwtStrategy, ExtractJwt } = passportJwt
|
|
90
|
+
const { passportJwtSecret } = jwksRsa
|
|
91
|
+
|
|
92
|
+
const jwtOptions = {
|
|
93
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
94
|
+
secretOrKeyProvider: passportJwtSecret({
|
|
95
|
+
cache: true,
|
|
96
|
+
rateLimit: true,
|
|
97
|
+
jwksRequestsPerMinute: 5,
|
|
98
|
+
jwksUri,
|
|
99
|
+
}),
|
|
100
|
+
issuer,
|
|
101
|
+
audience,
|
|
102
|
+
algorithms: algorithms ?? (['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'] as OIDCAlgorithm[]),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
passport.use(
|
|
106
|
+
this.strategy,
|
|
107
|
+
new JwtStrategy(jwtOptions, (payload: IOIDCTokenPayload, done: (error: any, user?: any, info?: any) => void) => {
|
|
108
|
+
if (payload) {
|
|
109
|
+
return done(null, payload)
|
|
110
|
+
}
|
|
111
|
+
return done('Bearer token not found or incorrect', null)
|
|
112
|
+
})
|
|
113
|
+
)
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Failed to initialize OIDC Bearer Auth:', error)
|
|
116
|
+
return Promise.reject(
|
|
117
|
+
new Error(
|
|
118
|
+
'Could not create JWT bearer strategy. Did you include "passport-jwt" and "jwks-rsa" dependencies in package.json?',
|
|
119
|
+
{ cause: error }
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async discoverJwksUri(issuer: string): Promise<string> {
|
|
126
|
+
const wellKnownUrl = `${issuer}${issuer.endsWith('/') ? '' : '/'}.well-known/openid-configuration`
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(wellKnownUrl)
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
return Promise.reject(
|
|
132
|
+
new Error(`Failed to fetch OIDC configuration from ${wellKnownUrl}: ${response.status} ${response.statusText}`)
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const config = (await response.json()) as { jwks_uri?: string }
|
|
137
|
+
if (!config.jwks_uri) {
|
|
138
|
+
return Promise.reject(new Error(`OIDC configuration at ${wellKnownUrl} does not contain jwks_uri`))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return config.jwks_uri
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return Promise.reject(
|
|
144
|
+
new Error(`Failed to discover JWKS URI from OIDC configuration at ${wellKnownUrl}`, { cause: error })
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|