@owlmeans/server-oidc-rp 0.1.0
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/LICENSE +21 -0
- package/README.md +520 -0
- package/build/.gitkeep +0 -0
- package/build/actions/index.d.ts +3 -0
- package/build/actions/index.d.ts.map +1 -0
- package/build/actions/index.js +3 -0
- package/build/actions/index.js.map +1 -0
- package/build/actions/init.d.ts +3 -0
- package/build/actions/init.d.ts.map +1 -0
- package/build/actions/init.js +68 -0
- package/build/actions/init.js.map +1 -0
- package/build/actions/process.d.ts +17 -0
- package/build/actions/process.d.ts.map +1 -0
- package/build/actions/process.js +80 -0
- package/build/actions/process.js.map +1 -0
- package/build/auth/consts.d.ts +2 -0
- package/build/auth/consts.d.ts.map +1 -0
- package/build/auth/consts.js +2 -0
- package/build/auth/consts.js.map +1 -0
- package/build/auth/index.d.ts +2 -0
- package/build/auth/index.d.ts.map +1 -0
- package/build/auth/index.js +2 -0
- package/build/auth/index.js.map +1 -0
- package/build/auth/plugins/index.d.ts +2 -0
- package/build/auth/plugins/index.d.ts.map +1 -0
- package/build/auth/plugins/index.js +6 -0
- package/build/auth/plugins/index.js.map +1 -0
- package/build/auth/plugins/oidc-client.d.ts +50 -0
- package/build/auth/plugins/oidc-client.d.ts.map +1 -0
- package/build/auth/plugins/oidc-client.js +211 -0
- package/build/auth/plugins/oidc-client.js.map +1 -0
- package/build/auth/plugins/types.d.ts +1 -0
- package/build/auth/plugins/types.d.ts.map +1 -0
- package/build/auth/plugins/types.js +2 -0
- package/build/auth/plugins/types.js.map +1 -0
- package/build/consts.d.ts +20 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +20 -0
- package/build/consts.js.map +1 -0
- package/build/gate.d.ts +3 -0
- package/build/gate.d.ts.map +1 -0
- package/build/gate.js +22 -0
- package/build/gate.js.map +1 -0
- package/build/guard.d.ts +6 -0
- package/build/guard.d.ts.map +1 -0
- package/build/guard.js +13 -0
- package/build/guard.js.map +1 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +7 -0
- package/build/index.js.map +1 -0
- package/build/model/consts.d.ts +6 -0
- package/build/model/consts.d.ts.map +1 -0
- package/build/model/consts.js +7 -0
- package/build/model/consts.js.map +1 -0
- package/build/model/gate.d.ts +4 -0
- package/build/model/gate.d.ts.map +1 -0
- package/build/model/gate.js +98 -0
- package/build/model/gate.js.map +1 -0
- package/build/model/types.d.ts +25 -0
- package/build/model/types.d.ts.map +1 -0
- package/build/model/types.js +2 -0
- package/build/model/types.js.map +1 -0
- package/build/models.d.ts +1 -0
- package/build/models.d.ts.map +1 -0
- package/build/models.js +2 -0
- package/build/models.js.map +1 -0
- package/build/modules.d.ts +3 -0
- package/build/modules.d.ts.map +1 -0
- package/build/modules.js +12 -0
- package/build/modules.js.map +1 -0
- package/build/service.d.ts +3 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +186 -0
- package/build/service.js.map +1 -0
- package/build/types.d.ts +55 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/auth.d.ts +5 -0
- package/build/utils/auth.d.ts.map +1 -0
- package/build/utils/auth.js +48 -0
- package/build/utils/auth.js.map +1 -0
- package/build/utils/cache.d.ts +8 -0
- package/build/utils/cache.d.ts.map +1 -0
- package/build/utils/cache.js +7 -0
- package/build/utils/cache.js.map +1 -0
- package/build/utils/index.d.ts +5 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +4 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/types.d.ts +10 -0
- package/build/utils/types.d.ts.map +1 -0
- package/build/utils/types.js +2 -0
- package/build/utils/types.js.map +1 -0
- package/build/utils/wrapped.d.ts +4 -0
- package/build/utils/wrapped.d.ts.map +1 -0
- package/build/utils/wrapped.js +3 -0
- package/build/utils/wrapped.js.map +1 -0
- package/build/wrapper.d.ts +3 -0
- package/build/wrapper.d.ts.map +1 -0
- package/build/wrapper.js +75 -0
- package/build/wrapper.js.map +1 -0
- package/package.json +71 -0
- package/src/actions/index.ts +3 -0
- package/src/actions/init.ts +90 -0
- package/src/actions/process.ts +113 -0
- package/src/auth/consts.ts +2 -0
- package/src/auth/index.ts +2 -0
- package/src/auth/plugins/index.ts +8 -0
- package/src/auth/plugins/oidc-client.ts +246 -0
- package/src/auth/plugins/types.ts +0 -0
- package/src/consts.ts +26 -0
- package/src/gate.ts +29 -0
- package/src/guard.ts +25 -0
- package/src/index.ts +7 -0
- package/src/model/consts.ts +6 -0
- package/src/model/gate.ts +129 -0
- package/src/model/types.ts +31 -0
- package/src/models.ts +1 -0
- package/src/modules.ts +30 -0
- package/src/service.ts +251 -0
- package/src/types.ts +64 -0
- package/src/utils/auth.ts +67 -0
- package/src/utils/cache.ts +12 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/types.ts +10 -0
- package/src/utils/wrapped.ts +6 -0
- package/src/wrapper.ts +92 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapped.js","sourceRoot":"","sources":["../../src/utils/wrapped.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAG7C,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAgB,EAAsB,EAAE,CAC9D,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAWzE,eAAO,MAAM,uBAAuB,QAAO,kBA4E1C,CAAA"}
|
package/build/wrapper.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AuthenFailed, AuthorizationError, AuthRole } from '@owlmeans/auth';
|
|
2
|
+
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope';
|
|
3
|
+
import { createService } from '@owlmeans/context';
|
|
4
|
+
import { OIDC_WRAPPED_TOKEN, WRAPPED_OIDC } from '@owlmeans/oidc';
|
|
5
|
+
import { cache, managedId } from './utils/cache.js';
|
|
6
|
+
import { authService, OIDC_AUTH_LIFTETIME, OIDC_WRAP_FRESHNESS } from './consts.js';
|
|
7
|
+
import days from 'dayjs';
|
|
8
|
+
import { TRUSTED } from '@owlmeans/config';
|
|
9
|
+
import { AUTH_SRV_KEY } from '@owlmeans/server-auth';
|
|
10
|
+
import { trust } from '@owlmeans/auth-common/utils';
|
|
11
|
+
export const makeOidcWrappingService = () => {
|
|
12
|
+
const service = createService(WRAPPED_OIDC, {
|
|
13
|
+
update: async (token, thr) => {
|
|
14
|
+
const ctx = service.assertCtx();
|
|
15
|
+
token = typeof token === 'string' ? token : token?.token;
|
|
16
|
+
if (token == null) {
|
|
17
|
+
throw new AuthorizationError('token');
|
|
18
|
+
}
|
|
19
|
+
const [, authentication] = token.split(' ');
|
|
20
|
+
const envelope = makeEnvelopeModel(authentication, EnvelopeKind.Token);
|
|
21
|
+
const user = envelope.message();
|
|
22
|
+
try {
|
|
23
|
+
const record = await cache(ctx).get(managedId(user.token));
|
|
24
|
+
if (record == null || record.payload == null) {
|
|
25
|
+
throw new AuthorizationError('record');
|
|
26
|
+
}
|
|
27
|
+
if (record.validated != null && days(record.validated)
|
|
28
|
+
.add(OIDC_WRAP_FRESHNESS, 'milliseconds').isAfter()) {
|
|
29
|
+
return { token };
|
|
30
|
+
}
|
|
31
|
+
record.validated = new Date();
|
|
32
|
+
const [update] = await ctx.module(authService.auth.update)
|
|
33
|
+
.call({ body: { token, tokenSet: record.payload } });
|
|
34
|
+
const updateEnvelope = makeEnvelopeModel(update.token, EnvelopeKind.Token);
|
|
35
|
+
const authManager = await trust(ctx, TRUSTED, AUTH_SRV_KEY);
|
|
36
|
+
if (!await updateEnvelope.verify(authManager.key)) {
|
|
37
|
+
throw new AuthenFailed();
|
|
38
|
+
}
|
|
39
|
+
const updatedAuth = updateEnvelope.message();
|
|
40
|
+
const updatedUser = {
|
|
41
|
+
...user,
|
|
42
|
+
role: updatedAuth.role ?? AuthRole.Guest,
|
|
43
|
+
userId: updatedAuth.userId ?? user.userId,
|
|
44
|
+
profileId: updatedAuth.profileId,
|
|
45
|
+
entityId: updatedAuth.entityId ?? user.entityId,
|
|
46
|
+
createdAt: new Date()
|
|
47
|
+
};
|
|
48
|
+
if (updatedAuth.challenge !== user.token) {
|
|
49
|
+
await cache(ctx).delete(record);
|
|
50
|
+
updatedUser.token = updatedAuth.challenge;
|
|
51
|
+
record.id = managedId(updatedUser.token);
|
|
52
|
+
}
|
|
53
|
+
const trusted = await trust(ctx, TRUSTED, ctx.cfg.alias ?? ctx.cfg.service);
|
|
54
|
+
const authorization = await makeEnvelopeModel(OIDC_WRAPPED_TOKEN)
|
|
55
|
+
.send(updatedUser, null).sign(trusted.key, EnvelopeKind.Token);
|
|
56
|
+
record.payload = update.tokenSet;
|
|
57
|
+
await cache(ctx).save(record, { ttl: OIDC_AUTH_LIFTETIME / 1000 });
|
|
58
|
+
return { token: `${OIDC_WRAPPED_TOKEN.toUpperCase()} ${authorization}` };
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(err);
|
|
62
|
+
await cache(ctx).delete(managedId(user.token));
|
|
63
|
+
if (thr) {
|
|
64
|
+
if (err instanceof AuthorizationError) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
throw new AuthorizationError('unknown');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return service;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/server-oidc-rp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 312 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
8
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
},
|
|
10
|
+
"main": "build/index.js",
|
|
11
|
+
"module": "build/index.js",
|
|
12
|
+
"types": "build/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./build/index.js",
|
|
16
|
+
"require": "./build/index.js",
|
|
17
|
+
"default": "./build/index.js",
|
|
18
|
+
"module": "./build/index.js",
|
|
19
|
+
"types": "./build/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./auth/plugins": {
|
|
22
|
+
"import": "./build/auth/plugins/index.js",
|
|
23
|
+
"require": "./build/auth/plugins/index.js",
|
|
24
|
+
"default": "./build/auth/plugins/index.js",
|
|
25
|
+
"module": "./build/auth/plugins/index.js",
|
|
26
|
+
"types": "./build/auth/plugins/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./auth": {
|
|
29
|
+
"import": "./build/auth/index.js",
|
|
30
|
+
"require": "./build/auth/index.js",
|
|
31
|
+
"default": "./build/auth/index.js",
|
|
32
|
+
"module": "./build/auth/index.js",
|
|
33
|
+
"types": "./build/auth/index.d.ts"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.7.8",
|
|
38
|
+
"nodemon": "^3.1.7",
|
|
39
|
+
"npm-check": "^6.0.1",
|
|
40
|
+
"typescript": "^5.6.3"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@noble/hashes": "^1.5.0",
|
|
44
|
+
"@owlmeans/auth": "^0.1.0",
|
|
45
|
+
"@owlmeans/auth-common": "^0.1.0",
|
|
46
|
+
"@owlmeans/basic-envelope": "^0.1.0",
|
|
47
|
+
"@owlmeans/client-module": "^0.1.0",
|
|
48
|
+
"@owlmeans/config": "^0.1.0",
|
|
49
|
+
"@owlmeans/context": "^0.1.0",
|
|
50
|
+
"@owlmeans/did": "^0.1.0",
|
|
51
|
+
"@owlmeans/module": "^0.1.0",
|
|
52
|
+
"@owlmeans/oidc": "^0.1.0",
|
|
53
|
+
"@owlmeans/resource": "^0.1.0",
|
|
54
|
+
"@owlmeans/route": "^0.1.0",
|
|
55
|
+
"@owlmeans/server-api": "^0.1.0",
|
|
56
|
+
"@owlmeans/server-auth": "^0.1.0",
|
|
57
|
+
"@owlmeans/server-context": "^0.1.0",
|
|
58
|
+
"@owlmeans/server-module": "^0.1.0",
|
|
59
|
+
"@scure/base": "^1.1.9",
|
|
60
|
+
"dayjs": "^1.11.13",
|
|
61
|
+
"jose": "^5.9.6",
|
|
62
|
+
"openid-client": "^6.1.1"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"ajv": "*"
|
|
66
|
+
},
|
|
67
|
+
"private": false,
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { RefedModuleHandler } from '@owlmeans/server-module'
|
|
2
|
+
import { handleBody } from '@owlmeans/server-api'
|
|
3
|
+
import type { OIDCAuthInitParams, OidcProviderDescriptor } from '@owlmeans/oidc'
|
|
4
|
+
import { AuthenPayloadError, AuthUnknown, DISPATCHER } from '@owlmeans/auth'
|
|
5
|
+
import { assertContext } from '@owlmeans/context'
|
|
6
|
+
import type { Config, Context, OidcClientAdapter, OidcClientService } from '../types.js'
|
|
7
|
+
import type { ClientModule } from '@owlmeans/client-module'
|
|
8
|
+
import { authService, DEFAULT_ALIAS } from '../consts.js'
|
|
9
|
+
// import { PROVIDER_CACHE_TTL } from '../consts.js'
|
|
10
|
+
// import type { Client } from 'openid-client'
|
|
11
|
+
|
|
12
|
+
import { base64urlnopad as base64 } from '@scure/base'
|
|
13
|
+
import { randomBytes } from '@noble/hashes/utils'
|
|
14
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
15
|
+
import { cache, verifierId } from '../utils/cache.js'
|
|
16
|
+
import { AUTHEN_TIMEFRAME } from '@owlmeans/server-auth'
|
|
17
|
+
|
|
18
|
+
export const init: RefedModuleHandler = handleBody(async (body: OIDCAuthInitParams, ctx) => {
|
|
19
|
+
if (body.entity == null) {
|
|
20
|
+
throw new AuthenPayloadError('entity')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const entityId = body.entity
|
|
24
|
+
|
|
25
|
+
const context = assertContext<Config, Context>(ctx)
|
|
26
|
+
const oidc = context.service<OidcClientService>(DEFAULT_ALIAS)
|
|
27
|
+
|
|
28
|
+
let client: OidcClientAdapter
|
|
29
|
+
// if (!oidc.hasProvider({ entityId })) {
|
|
30
|
+
/**
|
|
31
|
+
* @TODO We need to move it to some remote resource.
|
|
32
|
+
* And make oidc service itself use such a resource to get required client.
|
|
33
|
+
*/
|
|
34
|
+
const [providers] = await context.module<ClientModule<OidcProviderDescriptor[]>>(
|
|
35
|
+
authService.provider.list
|
|
36
|
+
).call({
|
|
37
|
+
params: { service: context.cfg.alias ?? context.cfg.service },
|
|
38
|
+
query: { entityId }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
if (providers.length < 1) {
|
|
42
|
+
throw new AuthUnknown()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log("\n\n ************* \n Providers we get from remote:", providers)
|
|
46
|
+
|
|
47
|
+
const provider = providers.find(p => p.def === true) ?? providers.shift()
|
|
48
|
+
|
|
49
|
+
console.log("\n\nProvider we choose:", provider)
|
|
50
|
+
console.log( "************* \n\n")
|
|
51
|
+
|
|
52
|
+
if (provider == null) {
|
|
53
|
+
throw new AuthUnknown()
|
|
54
|
+
}
|
|
55
|
+
oidc.registerTemporaryProvider(provider)
|
|
56
|
+
client = await oidc.getClient({ clientId: provider.clientId, entityId })
|
|
57
|
+
// Cache provider for a while - we dont actually need to clean it up
|
|
58
|
+
// setTimeout(() => provider && oidc.unregisterTemporaryProvider(provider), PROVIDER_CACHE_TTL)
|
|
59
|
+
// } else {
|
|
60
|
+
// client = await oidc.getClient({
|
|
61
|
+
// entityId, clientId: oidc.entityToClientId({ entityId })
|
|
62
|
+
// })
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
const verifier = base64.encode(randomBytes(32))
|
|
66
|
+
const challenge = base64.encode(sha256(verifier))
|
|
67
|
+
|
|
68
|
+
console.log("\n\n We create verifierID: ", verifierId(challenge), verifier, " with client: ", client.getClientId(), "\n\n")
|
|
69
|
+
|
|
70
|
+
await cache(context).create({
|
|
71
|
+
id: verifierId(challenge),
|
|
72
|
+
verifier,
|
|
73
|
+
client: client.getClientId(),
|
|
74
|
+
entityId,
|
|
75
|
+
}, { ttl: AUTHEN_TIMEFRAME / 1000 })
|
|
76
|
+
|
|
77
|
+
const [dispatcherUrl] = await context.module<ClientModule<string>>(DISPATCHER).call()
|
|
78
|
+
|
|
79
|
+
const cfg = client.getConfig()
|
|
80
|
+
const url = client.makeAuthUrl({
|
|
81
|
+
scope: `openid profile email ${cfg?.extraScopes ?? ''}`,
|
|
82
|
+
code_challenge: challenge,
|
|
83
|
+
code_challenge_method: 'S256',
|
|
84
|
+
redirect_uri: dispatcherUrl,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
console.log('Url we got', url)
|
|
88
|
+
|
|
89
|
+
return url
|
|
90
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { RefedModuleHandler } from '@owlmeans/server-module'
|
|
2
|
+
import { handleBody } from '@owlmeans/server-api'
|
|
3
|
+
import type { OIDCClientAuthPayload } from '@owlmeans/oidc'
|
|
4
|
+
import { OIDC_WRAPPED_TOKEN } from '@owlmeans/oidc'
|
|
5
|
+
import { assertContext } from '@owlmeans/context'
|
|
6
|
+
import type { Config, Context } from '../types.js'
|
|
7
|
+
import Url from 'url'
|
|
8
|
+
import { makeOidcAuthentication } from '../utils/auth.js'
|
|
9
|
+
import type { Auth, AuthCredentials } from '@owlmeans/auth'
|
|
10
|
+
import { AuthenFailed, AuthManagerError, AuthRole } from '@owlmeans/auth'
|
|
11
|
+
import { decodeJwt } from 'jose'
|
|
12
|
+
import { cache, exchangeId, managedId } from '../utils/cache.js'
|
|
13
|
+
import { trust } from '@owlmeans/auth-common/utils'
|
|
14
|
+
import { TRUSTED } from '@owlmeans/config'
|
|
15
|
+
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
|
|
16
|
+
import { OIDC_AUTH_LIFTETIME } from '../consts.js'
|
|
17
|
+
import { wrapper } from '../utils/wrapped.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Authentication method links together OIDC and OwlMeans auth.
|
|
21
|
+
* 1. We authenticate OIDC redirect back params with the classic OIDC client.
|
|
22
|
+
* 2. We temporary store on the server refresh token and OIDC token.
|
|
23
|
+
* 3. We supply the client with perked OwlMeans Auth token that in fact
|
|
24
|
+
* is the set of OIDC tokens (except refresh token - we keep it here?)
|
|
25
|
+
* 4. We used it in a centralized way to check permissions over OwlMeans Auth
|
|
26
|
+
* central service irrespectively is it OIDC or our custom permissions engine
|
|
27
|
+
* (this is useful to properly support paid accounts and bridge the underlying
|
|
28
|
+
* IAM system implemetation - abstract from it).
|
|
29
|
+
* 5. The method ends with production of OwlMeans authorization token.
|
|
30
|
+
*
|
|
31
|
+
* ! There is also token renewal mechanism that should be implemented nearby.
|
|
32
|
+
*/
|
|
33
|
+
export const authenticate: RefedModuleHandler = handleBody(async (
|
|
34
|
+
{ authUrl, ...params }: OIDCClientAuthPayload,
|
|
35
|
+
ctx
|
|
36
|
+
) => {
|
|
37
|
+
const context = assertContext<Config, Context>(ctx)
|
|
38
|
+
|
|
39
|
+
const url = new Url.URL(authUrl)
|
|
40
|
+
const redirectUrl = url.searchParams.get('redirect_uri')
|
|
41
|
+
|
|
42
|
+
const [cfg, tokenSet, token] = await makeOidcAuthentication(context)({
|
|
43
|
+
challenge: redirectUrl + ':' + authUrl,
|
|
44
|
+
credential: new Url.URLSearchParams(params).toString()
|
|
45
|
+
} as AuthCredentials)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
console.log("\n\n ============ \n Config we got for real autentioncation", cfg)
|
|
49
|
+
|
|
50
|
+
if (tokenSet.id_token == null || tokenSet.access_token == null) {
|
|
51
|
+
throw new AuthenFailed()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (cfg.service == null) {
|
|
55
|
+
throw new AuthManagerError('service')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await cache(context).delete(exchangeId(token))
|
|
59
|
+
|
|
60
|
+
console.log(
|
|
61
|
+
'List provided user permisssions',
|
|
62
|
+
decodeJwt(tokenSet.access_token)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const id = decodeJwt(tokenSet.id_token)
|
|
66
|
+
console.log(
|
|
67
|
+
'token set',
|
|
68
|
+
tokenSet,
|
|
69
|
+
tokenSet.claims(),
|
|
70
|
+
id
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
let user: Auth = {
|
|
74
|
+
type: OIDC_WRAPPED_TOKEN,
|
|
75
|
+
token,
|
|
76
|
+
// @TODO Actually this is highly incorrect - we need to get profile details
|
|
77
|
+
// from the OwlMeans Auth intead ?
|
|
78
|
+
userId: id.sub as string,
|
|
79
|
+
// @TODO Actually we should check is it user or admin or even guest
|
|
80
|
+
// and set proper role
|
|
81
|
+
role: AuthRole.Guest,
|
|
82
|
+
// @TODO It's unclear if it's secure - but it's not used right now
|
|
83
|
+
scopes: [
|
|
84
|
+
...(context.cfg.shortAlias != null ? [context.cfg.shortAlias] : []),
|
|
85
|
+
...(context.cfg.alias != null ? [context.cfg.alias] : []),
|
|
86
|
+
context.cfg.service,
|
|
87
|
+
cfg.service,
|
|
88
|
+
],
|
|
89
|
+
source: `${cfg.service}/${cfg.clientId}${'id' in cfg ? `/${cfg.id}` : ''}`,
|
|
90
|
+
// @TODO Actually this is highly incorrect - we need to get profile details
|
|
91
|
+
// from the OwlMeans Auth intead
|
|
92
|
+
// profileId: user?.userId,
|
|
93
|
+
entityId: cfg.entityId,
|
|
94
|
+
isUser: true,
|
|
95
|
+
createdAt: new Date(),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log('>>>>> ~~~~~ STORE RECORD WITH ID: ', managedId(token))
|
|
99
|
+
await cache(context).create(
|
|
100
|
+
{ id: managedId(token), payload: tokenSet, client: cfg.clientId },
|
|
101
|
+
{ ttl: OIDC_AUTH_LIFTETIME / 1000 }
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
const trusted = await trust<Config, Context>(context, TRUSTED, context.cfg.alias ?? context.cfg.service)
|
|
105
|
+
const authorization = await makeEnvelopeModel<Auth>(OIDC_WRAPPED_TOKEN)
|
|
106
|
+
.send(user, null).sign(trusted.key, EnvelopeKind.Token)
|
|
107
|
+
|
|
108
|
+
let auth = { token: `${OIDC_WRAPPED_TOKEN.toUpperCase()} ${authorization}` }
|
|
109
|
+
|
|
110
|
+
auth = await wrapper(context).update(auth, true) ?? auth
|
|
111
|
+
|
|
112
|
+
return auth
|
|
113
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
import { plugins as authPlugins } from '@owlmeans/server-auth/manager'
|
|
3
|
+
import { oidcClientPlugin } from './oidc-client.js'
|
|
4
|
+
import { OIDC_CLIENT_AUTH } from '@owlmeans/oidc'
|
|
5
|
+
|
|
6
|
+
authPlugins[OIDC_CLIENT_AUTH] = oidcClientPlugin as typeof authPlugins[keyof typeof authPlugins]
|
|
7
|
+
|
|
8
|
+
export const plugins = authPlugins
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
|
|
2
|
+
import type { AuthPlugin } from '@owlmeans/server-auth/manager/plugins'
|
|
3
|
+
import type { Config, Context, OidcClientService } from '../../types.js'
|
|
4
|
+
import { assertType } from '@owlmeans/server-auth/manager/plugins'
|
|
5
|
+
import { DEFAULT_ALIAS } from '../../consts.js'
|
|
6
|
+
import { OIDC_CLIENT_AUTH } from '@owlmeans/oidc'
|
|
7
|
+
import type { OidcUserDetails, ProviderProfileDetails } from '@owlmeans/oidc'
|
|
8
|
+
import { base64urlnopad as base64 } from '@scure/base'
|
|
9
|
+
import { randomBytes } from '@noble/hashes/utils'
|
|
10
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
11
|
+
import { ALL_SCOPES, AuthenFailed, AuthenPayloadError, AuthManagerError, AuthRole } from '@owlmeans/auth'
|
|
12
|
+
import { AUTHEN_TIMEFRAME } from '@owlmeans/server-auth'
|
|
13
|
+
import { decodeJwt } from 'jose'
|
|
14
|
+
import { KEY_OWL } from '@owlmeans/did'
|
|
15
|
+
import { cache, verifierId } from '../../utils/cache.js'
|
|
16
|
+
import { makeOidcAuthentication } from '../../utils/auth.js'
|
|
17
|
+
// import { URL } from 'url'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Actually the most of implementation desceibed here is a proprietary one and
|
|
21
|
+
* located within OwlMeans Auth service implmentation.
|
|
22
|
+
* We keep it here for easier code navigation and understanding of relying developers
|
|
23
|
+
* about what's going on but we can't show it.
|
|
24
|
+
*
|
|
25
|
+
* @See OwlMeans Auth {AccountLinkingService} interface implementation for more details
|
|
26
|
+
*
|
|
27
|
+
* We need to distinguish the following cases:
|
|
28
|
+
* 1. The user authenticates via owlmenas.org
|
|
29
|
+
* 1.1. The user authenticated in owlmeans.net via IAM product based account.
|
|
30
|
+
* 1.1.1. The user do it for the first time
|
|
31
|
+
* * We create an account
|
|
32
|
+
* * We create and link credentials from jwt to the account (i'm not sure if it's required)
|
|
33
|
+
* * We create the profile
|
|
34
|
+
* * We create and link credentials from jwt to the profile
|
|
35
|
+
* 1.1.2. The user do it not for the first time
|
|
36
|
+
* * We found the linked profile by jwt
|
|
37
|
+
* 1.2. The user autehnticated in owlmeans.net via OwlMeans ID
|
|
38
|
+
* 1.2.1. The user do it for the first time
|
|
39
|
+
* * Figure out if the user has OwlMeans ID linked to the authentication from jwt
|
|
40
|
+
* (GET /admin/realms/{realm}/users/{id}/federated-identity)
|
|
41
|
+
* * We create and link credentials from jwt to the profile
|
|
42
|
+
* 1.2.2. The user do it not for the first time
|
|
43
|
+
* * We found the linked profile by credentials from jwt
|
|
44
|
+
* 2. The user authenticates via a custom identity provider
|
|
45
|
+
* 2.1. The user auhtneticated in his identity provier directly
|
|
46
|
+
* 2.1.1. The user do it for the first time
|
|
47
|
+
* * We create a profile (we cant request info about his Owlmeans id)!
|
|
48
|
+
* * We create and link credentials from jwt to the profile
|
|
49
|
+
* * ! When the user will try to pay for something via this profile
|
|
50
|
+
* * we need to force him to link it to owlmeans.net profile / account.
|
|
51
|
+
* 2.1.2. The user do it not for the first time
|
|
52
|
+
* * We found the linked profile by jwt
|
|
53
|
+
* 2.2. The user authenticated in his identity provider via OwlMeans ID
|
|
54
|
+
* - This case actually shouldn't know anything about it until the profile is linked to the
|
|
55
|
+
* owlmeans.net acccount.
|
|
56
|
+
*
|
|
57
|
+
* Generailized
|
|
58
|
+
* 1. We try to get credentials
|
|
59
|
+
* 2. If there is no credentials we try to get more info abou the user
|
|
60
|
+
* 2.1. To do it we get admin client if it's presented for API access
|
|
61
|
+
* 2.2. If it's presented we get list of identity providers to figure out if there an owlmeans.net id
|
|
62
|
+
* 3. If there is no owlmeans.net account while the login is going through it - we create one
|
|
63
|
+
* 4. If there is no profile - we create one and link oidc credentials
|
|
64
|
+
*/
|
|
65
|
+
export const oidcClientPlugin = <C extends Config, T extends Context<C>>(context: T): AuthPlugin => {
|
|
66
|
+
const authenticate = makeOidcAuthentication<C, T>(context)
|
|
67
|
+
|
|
68
|
+
const plugin: AuthPlugin = {
|
|
69
|
+
type: OIDC_CLIENT_AUTH,
|
|
70
|
+
|
|
71
|
+
init: async request => {
|
|
72
|
+
assertType(request.type, plugin)
|
|
73
|
+
|
|
74
|
+
if (context.cfg.oidc.restrictedProviders === false) {
|
|
75
|
+
throw new AuthManagerError('oidc.internal')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// @TODO Actually think about how to make oidc service configurable
|
|
79
|
+
const oidc = context.service<OidcClientService>(DEFAULT_ALIAS)
|
|
80
|
+
let entityId = request.entityId ?? oidc.getDefault()
|
|
81
|
+
|
|
82
|
+
if (entityId == null) {
|
|
83
|
+
throw new AuthenPayloadError('client')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (context.cfg.oidc.restrictedProviders === true && oidc.getDefault() !== entityId) {
|
|
87
|
+
throw new AuthenPayloadError('client')
|
|
88
|
+
}
|
|
89
|
+
if (Array.isArray(context.cfg.oidc.restrictedProviders)
|
|
90
|
+
&& !context.cfg.oidc.restrictedProviders.includes(entityId)) {
|
|
91
|
+
throw new AuthenPayloadError('client')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const verifier = base64.encode(randomBytes(32))
|
|
95
|
+
const challenge = base64.encode(sha256(verifier))
|
|
96
|
+
|
|
97
|
+
console.log("\n\n We create verifierID (2): ", verifierId(challenge), verifier, " with client: ", entityId, "\n\n")
|
|
98
|
+
|
|
99
|
+
await cache<C, T>(context).create({
|
|
100
|
+
id: verifierId(challenge),
|
|
101
|
+
verifier,
|
|
102
|
+
client: entityId
|
|
103
|
+
}, { ttl: AUTHEN_TIMEFRAME / 1000 })
|
|
104
|
+
|
|
105
|
+
if (request.source == null) {
|
|
106
|
+
throw new AuthenPayloadError('redirect-uri')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// const redirectUrl = new URL(request.source)
|
|
110
|
+
|
|
111
|
+
// for (let key of redirectUrl.searchParams.keys()) {
|
|
112
|
+
// redirectUrl.searchParams.delete(key)
|
|
113
|
+
// }
|
|
114
|
+
|
|
115
|
+
const client = await oidc.getClient(entityId)
|
|
116
|
+
const cfg = client.getConfig()
|
|
117
|
+
const url = client.makeAuthUrl({
|
|
118
|
+
scope: `openid profile email ${cfg?.extraScopes ?? ''}`,
|
|
119
|
+
code_challenge: challenge,
|
|
120
|
+
code_challenge_method: 'S256',
|
|
121
|
+
redirect_uri: request.source,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
return { challenge: url }
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
authenticate: async credential => {
|
|
128
|
+
console.log('000000000000000')
|
|
129
|
+
const [cfg, tokenSet, exchangeToken] = await authenticate(credential)
|
|
130
|
+
|
|
131
|
+
if (tokenSet.id_token == null || tokenSet.access_token == null) {
|
|
132
|
+
throw new AuthenFailed()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const oidc = context.service<OidcClientService>(DEFAULT_ALIAS)
|
|
136
|
+
const jwt = decodeJwt(tokenSet.id_token)
|
|
137
|
+
|
|
138
|
+
if (jwt.sub == null) {
|
|
139
|
+
throw new AuthenFailed()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const store = oidc.accountLinking()
|
|
143
|
+
if (store == null) {
|
|
144
|
+
throw new AuthManagerError('oidc.account.store')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (cfg.service == null) {
|
|
148
|
+
throw new AuthManagerError('oidc.service')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let profile = await store.getLinkedProfile({
|
|
152
|
+
type: OIDC_CLIENT_AUTH,
|
|
153
|
+
service: cfg.service,
|
|
154
|
+
clientId: cfg.clientId,
|
|
155
|
+
userId: jwt.sub
|
|
156
|
+
} satisfies ProviderProfileDetails)
|
|
157
|
+
|
|
158
|
+
// @TODO It's likely the case when we are talking about owlmeans.net
|
|
159
|
+
// but some addtional checks need to be done.
|
|
160
|
+
// The problem is that we will make the sold identity providers also
|
|
161
|
+
// managable, so this condition isn't enough to cover described cases.
|
|
162
|
+
let details: OidcUserDetails
|
|
163
|
+
if (cfg.apiClientId != null) {
|
|
164
|
+
console.log(1)
|
|
165
|
+
const apiClient = await oidc.getClient(cfg.apiClientId)
|
|
166
|
+
const adminTokens = await apiClient.grantWithCredentials()
|
|
167
|
+
if (adminTokens.access_token == null) {
|
|
168
|
+
throw new AuthManagerError('iam.admin')
|
|
169
|
+
}
|
|
170
|
+
const api = oidc.providerApi()
|
|
171
|
+
if (api == null) {
|
|
172
|
+
// @TODO And what if don't? Probably we can still do something here
|
|
173
|
+
throw new AuthManagerError('iam.admin.api')
|
|
174
|
+
}
|
|
175
|
+
details = await api.getUserDetails(adminTokens.access_token, jwt.sub)
|
|
176
|
+
} else {
|
|
177
|
+
// @TODO We need to do something here if it's an external identity provider
|
|
178
|
+
// and we couldn't get any addiontal info
|
|
179
|
+
throw new AuthManagerError('implemented')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
// Create profile
|
|
184
|
+
profile == null
|
|
185
|
+
// Fix profile if it's not linked properly to OwlMeans Id
|
|
186
|
+
|| (details.did != null && details.isOwlMeansId && !profile.profileId?.startsWith(KEY_OWL + ':'))
|
|
187
|
+
) {
|
|
188
|
+
console.log(2)
|
|
189
|
+
details.isOwlMeansId ??= cfg.entityId != null && cfg.entityId === context.cfg.defaultEntityId
|
|
190
|
+
if (details.isOwlMeansId && context.cfg.defaultEntityId == null) {
|
|
191
|
+
throw new AuthManagerError('iam.governance')
|
|
192
|
+
}
|
|
193
|
+
details.entityId = details.isOwlMeansId ? context.cfg.defaultEntityId : cfg.entityId
|
|
194
|
+
if (details.entityId == null) {
|
|
195
|
+
// @TODO We doesn't support clients and entites that aren't bound to an organization
|
|
196
|
+
// right now. Probably we should. Probably there are some cases when we have to.
|
|
197
|
+
throw new AuthManagerError('iam.entity')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
console.log('Supper attention ~%~: ', details.entityId, cfg.entityId, context.cfg.defaultEntityId)
|
|
202
|
+
|
|
203
|
+
// We relink existing profile only if it's not yet did bound
|
|
204
|
+
// and the orgnaization is owlmeans.net
|
|
205
|
+
if (profile == null || (
|
|
206
|
+
details.entityId === context.cfg.defaultEntityId
|
|
207
|
+
// @TODO Figure out more flexible solution.
|
|
208
|
+
// This check prevent a potenatilly vulnarability when a user can
|
|
209
|
+
// connect its own idp to his arbitrary IAM system that is connected as
|
|
210
|
+
// idp to our IAM system, and create an internal idp link that
|
|
211
|
+
// uses owlmeans did as user id of another user to link an arbitrary
|
|
212
|
+
// profile to his or her owlmeans user account and take adavantage of cross login.
|
|
213
|
+
// Probably such cross login accross owner like profiles should be prevented
|
|
214
|
+
// without additional authentication. But for now, this check guaranty that it works
|
|
215
|
+
// only with owlmeans.net idp that is managed by ours.
|
|
216
|
+
&& details.entityId === cfg.entityId
|
|
217
|
+
)) {
|
|
218
|
+
console.log(3, details)
|
|
219
|
+
profile = await store.linkProfile({
|
|
220
|
+
...details,
|
|
221
|
+
service: cfg.service,
|
|
222
|
+
clientId: cfg.clientId,
|
|
223
|
+
type: OIDC_CLIENT_AUTH,
|
|
224
|
+
}, {
|
|
225
|
+
username: jwt.preferred_username as string ?? details.username,
|
|
226
|
+
// @TODO It's important to keep it this way to make registration disabling working
|
|
227
|
+
force: false
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
console.log(4)
|
|
232
|
+
|
|
233
|
+
credential.scopes = [ALL_SCOPES]
|
|
234
|
+
credential.source = cfg.clientId
|
|
235
|
+
credential.role = AuthRole.User
|
|
236
|
+
credential.type = OIDC_CLIENT_AUTH
|
|
237
|
+
credential.userId = profile.userId
|
|
238
|
+
credential.profileId = profile.profileId
|
|
239
|
+
credential.entityId = profile.entityId
|
|
240
|
+
|
|
241
|
+
return { token: exchangeToken }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return plugin
|
|
246
|
+
}
|
|
File without changes
|