@owlmeans/server-oidc-provider 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 +848 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +3 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +3 -0
- package/build/consts.js.map +1 -0
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/build/middleware.d.ts +3 -0
- package/build/middleware.d.ts.map +1 -0
- package/build/middleware.js +24 -0
- package/build/middleware.js.map +1 -0
- package/build/service.d.ts +4 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +78 -0
- package/build/service.js.map +1 -0
- package/build/types.d.ts +46 -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/client.d.ts +4 -0
- package/build/utils/client.d.ts.map +1 -0
- package/build/utils/client.js +31 -0
- package/build/utils/client.js.map +1 -0
- package/build/utils/config.d.ts +4 -0
- package/build/utils/config.d.ts.map +1 -0
- package/build/utils/config.js +39 -0
- package/build/utils/config.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/package.json +49 -0
- package/src/consts.ts +4 -0
- package/src/index.ts +5 -0
- package/src/middleware.ts +29 -0
- package/src/service.ts +103 -0
- package/src/types.ts +55 -0
- package/src/utils/client.ts +40 -0
- package/src/utils/config.ts +43 -0
- package/src/utils/index.ts +3 -0
- package/tsconfig.json +15 -0
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,eAAO,MAAM,oBAAoB,yBAA0B,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAA;AAE5C,MAAM,CAAC,MAAM,oBAAoB,GAAI,sBAAsB,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAOnD,eAAO,MAAM,4BAA4B,SAAS,MAAM,8BAqBvD,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { assertContext, MiddlewareStage, MiddlewareType } from '@owlmeans/context';
|
|
2
|
+
import { DEFAULT_ALIAS as WEB_ALIAS } from '@owlmeans/server-api';
|
|
3
|
+
import { DEFAULT_ALIAS } from './consts.js';
|
|
4
|
+
export const createOidcProviderMiddleware = (web = WEB_ALIAS, oidc = DEFAULT_ALIAS) => {
|
|
5
|
+
const middleware = {
|
|
6
|
+
type: MiddlewareType.Context,
|
|
7
|
+
stage: MiddlewareStage.Loading,
|
|
8
|
+
apply: async (ctx) => {
|
|
9
|
+
const context = assertContext(ctx);
|
|
10
|
+
const webService = context.service(web);
|
|
11
|
+
const oidcService = context.service(oidc);
|
|
12
|
+
const marker = `__oidcServiceAdded-${web}-${oidc}`;
|
|
13
|
+
if (!ctx.cfg.records?.find(record => record.id === marker)) {
|
|
14
|
+
await oidcService.update(webService);
|
|
15
|
+
if (ctx.cfg.records == null) {
|
|
16
|
+
ctx.cfg.records = [];
|
|
17
|
+
}
|
|
18
|
+
ctx.cfg.records.push({ id: marker });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return middleware;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClF,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,MAAc,SAAS,EAAE,IAAI,GAAG,aAAa,EAAE,EAAE;IAC5F,MAAM,UAAU,GAAe;QAC7B,IAAI,EAAE,cAAc,CAAC,OAAO;QAC5B,KAAK,EAAE,eAAe,CAAC,OAAO;QAC9B,KAAK,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;YACjB,MAAM,OAAO,GAAG,aAAa,CAAkB,GAAyB,CAAY,CAAA;YACpF,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAY,GAAG,CAAC,CAAA;YAClD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAsB,IAAI,CAAC,CAAA;YAE9D,MAAM,MAAM,GAAG,sBAAsB,GAAG,IAAI,IAAI,EAAE,CAAA;YAClD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;gBAC3D,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;gBACpC,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;oBAC5B,GAAG,CAAC,GAAG,CAAC,OAAO,GAAG,EAAE,CAAA;gBACtB,CAAC;gBACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;YACtC,CAAC;QACH,CAAC;KACF,CAAA;IAED,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Config, Context, OidcProviderService } from './types.js';
|
|
2
|
+
export declare const createOidcProviderService: (alias?: string) => OidcProviderService;
|
|
3
|
+
export declare const appendOidcProviderService: <C extends Config, T extends Context<C>>(ctx: T, alias?: string) => T;
|
|
4
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAA0C,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAS9G,eAAO,MAAM,yBAAyB,WAAW,MAAM,KAAmB,mBA+EzE,CAAA;AAED,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OACzE,CAAC,UAAS,MAAM,KACpB,CAOF,CAAA"}
|
package/build/service.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { assertContext, createService } from '@owlmeans/context';
|
|
2
|
+
import { DEFAULT_ALIAS, OIDC_ACCOUNT_SERVICE } from './consts.js';
|
|
3
|
+
import { DEFAULT_PATH, INTERACTION } from '@owlmeans/oidc';
|
|
4
|
+
import Provider from 'oidc-provider';
|
|
5
|
+
import { SEP } from '@owlmeans/route';
|
|
6
|
+
import { makeSecurityHelper } from '@owlmeans/config';
|
|
7
|
+
import { combineConfig } from './utils/config.js';
|
|
8
|
+
let _initializedOidc = undefined;
|
|
9
|
+
export const createOidcProviderService = (alias = DEFAULT_ALIAS) => {
|
|
10
|
+
const service = createService(alias, {
|
|
11
|
+
update: async (api) => {
|
|
12
|
+
const context = assertContext(service.ctx, alias);
|
|
13
|
+
const cfg = context.cfg.oidc;
|
|
14
|
+
const serviceRoute = context.cfg.services[cfg.authService ?? context.cfg.service];
|
|
15
|
+
const helper = makeSecurityHelper(context);
|
|
16
|
+
const url = helper.makeUrl(serviceRoute, cfg.basePath ?? DEFAULT_PATH, { base: true });
|
|
17
|
+
const unsecure = context.cfg.security?.unsecure === false ? false : !url.startsWith('https');
|
|
18
|
+
const oidc = new Provider(url, {
|
|
19
|
+
...await combineConfig(context, unsecure),
|
|
20
|
+
adapter: cfg.adapterService != null
|
|
21
|
+
? name => context.service(cfg.adapterService).instance(name)
|
|
22
|
+
: undefined,
|
|
23
|
+
findAccount: async (_, id, _token) => {
|
|
24
|
+
const accountSrv = context.service(cfg.accountService ?? OIDC_ACCOUNT_SERVICE);
|
|
25
|
+
return accountSrv.loadById(context, id);
|
|
26
|
+
},
|
|
27
|
+
interactions: {
|
|
28
|
+
url: async (_, interaction) => {
|
|
29
|
+
const module = context.module(INTERACTION);
|
|
30
|
+
const [uri] = await module.call({ params: { uid: interaction.uid } });
|
|
31
|
+
return uri;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
oidc.proxy = cfg.behindProxy ?? unsecure;
|
|
36
|
+
const base = SEP + (cfg.basePath ?? DEFAULT_PATH);
|
|
37
|
+
await api.server.use(base, oidc.callback());
|
|
38
|
+
oidc.use(async (ctx, next) => {
|
|
39
|
+
await next();
|
|
40
|
+
const csp = ctx.response.headers['content-security-policy'];
|
|
41
|
+
if (csp != null) {
|
|
42
|
+
// @TODO Make it a little bit nicer - preferably using helmet :)
|
|
43
|
+
ctx.response.set('Content-Security-Policy', csp.replace(/form-action 'self'/, 'form-action *'));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (context.cfg.debug?.all || context.cfg.debug?.oidc) {
|
|
47
|
+
oidc.use(async (_, next) => {
|
|
48
|
+
await next();
|
|
49
|
+
});
|
|
50
|
+
oidc.on('grant.error', (_, error) => {
|
|
51
|
+
console.warn('GRANT ERROR .......: ');
|
|
52
|
+
console.info(oidc.issuer, _.request.toJSON(), _.body);
|
|
53
|
+
console.error('!!!! GRANT ERROR: ', error);
|
|
54
|
+
});
|
|
55
|
+
oidc.on('server_error', (ctx, error) => {
|
|
56
|
+
console.warn('SERVER ERROR .......: ', Object.getOwnPropertyNames(ctx.oidc));
|
|
57
|
+
console.info(ctx.oidc.grant);
|
|
58
|
+
console.error('!!!! SERVER ERROR: ', error);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
_initializedOidc = service.oidc = oidc;
|
|
62
|
+
},
|
|
63
|
+
instance: () => {
|
|
64
|
+
return service.oidc ?? (service.oidc = _initializedOidc);
|
|
65
|
+
},
|
|
66
|
+
getInteraction: async (id) => {
|
|
67
|
+
return await service.instance().Interaction.find(id) ?? null;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return service;
|
|
71
|
+
};
|
|
72
|
+
export const appendOidcProviderService = (ctx, alias = DEFAULT_ALIAS) => {
|
|
73
|
+
const service = createOidcProviderService(alias);
|
|
74
|
+
const context = ctx;
|
|
75
|
+
context.registerService(service);
|
|
76
|
+
return context;
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { InitializedService } from '@owlmeans/context';
|
|
2
|
+
import type { OidcSharedConfig } from '@owlmeans/oidc';
|
|
3
|
+
import type { ApiServer, ApiServerAppend } from '@owlmeans/server-api';
|
|
4
|
+
import type { ServerConfig, ServerContext } from '@owlmeans/server-context';
|
|
5
|
+
import type { Account, Adapter, ClientMetadata, Configuration, Interaction, Provider } from 'oidc-provider';
|
|
6
|
+
export interface OidcProviderService extends InitializedService {
|
|
7
|
+
oidc: Provider;
|
|
8
|
+
update: (api: ApiServer) => Promise<void>;
|
|
9
|
+
instance: () => Provider;
|
|
10
|
+
getInteraction: (id: string) => Promise<Interaction | null>;
|
|
11
|
+
}
|
|
12
|
+
export interface OidcConfigAppend<Extra extends OidcSharedConfig = OidcSharedConfig> {
|
|
13
|
+
oidc: OidcConfig & Extra;
|
|
14
|
+
}
|
|
15
|
+
export interface OidcConfig extends OidcSharedConfig {
|
|
16
|
+
authService?: string;
|
|
17
|
+
basePath?: string;
|
|
18
|
+
frontBase?: string;
|
|
19
|
+
clients: ClientMetadata[];
|
|
20
|
+
customConfiguration?: Configuration;
|
|
21
|
+
behindProxy?: boolean;
|
|
22
|
+
defaultKeys: {
|
|
23
|
+
RS256: {
|
|
24
|
+
pk: string;
|
|
25
|
+
pub?: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
accountService?: string;
|
|
29
|
+
adapterService?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface OidcAccountService extends InitializedService {
|
|
32
|
+
loadById: <C extends Config, T extends Context<C>>(ctx: T, id: string) => Promise<Account | undefined>;
|
|
33
|
+
}
|
|
34
|
+
export interface OidcAdapterService extends InitializedService {
|
|
35
|
+
instance: (name: string) => Adapter;
|
|
36
|
+
}
|
|
37
|
+
export interface Config extends ServerConfig, OidcConfigAppend {
|
|
38
|
+
debug: ServerConfig["debug"] & {
|
|
39
|
+
oidc?: boolean;
|
|
40
|
+
oidcServer?: boolean;
|
|
41
|
+
oidcData?: boolean;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface Context<C extends Config = Config> extends ServerContext<C>, ApiServerAppend {
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE3G,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,IAAI,EAAE,QAAQ,CAAA;IAEd,MAAM,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzC,QAAQ,EAAE,MAAM,QAAQ,CAAA;IAExB,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;CAC5D;AAED,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,gBAAgB,GAAG,gBAAgB;IACjF,IAAI,EAAE,UAAU,GAAG,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,UAAW,SAAQ,gBAAgB;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,mBAAmB,CAAC,EAAE,aAAa,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,EAAE;QACX,KAAK,EAAE;YACL,EAAE,EAAE,MAAM,CAAA;YACV,GAAG,CAAC,EAAE,MAAM,CAAA;SACb,CAAA;KACF,CAAA;IACD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;CACvG;AAED,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CACpC;AAED,MAAM,WAAW,MAAO,SAAQ,YAAY,EAAE,gBAAgB;IAC5D,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG;QAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC,EACxE,eAAe;CAAI"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAU,OAAO,EAAE,MAAM,aAAa,CAAA;AAKlD,eAAO,MAAM,YAAY,YAAa,OAAO,UAAU,cAAc,KAAG,cAoBvE,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { randomBytes } from '@noble/hashes/utils';
|
|
2
|
+
import { hex } from '@scure/base';
|
|
3
|
+
import { makeSecurityHelper } from '@owlmeans/config';
|
|
4
|
+
import { SEP } from '@owlmeans/route';
|
|
5
|
+
export const updateClient = (context, client) => {
|
|
6
|
+
if (client.client_secret == null) {
|
|
7
|
+
if (!context.cfg.debug.all && !context.cfg.debug.oidc) {
|
|
8
|
+
throw new SyntaxError('Client secret is required');
|
|
9
|
+
}
|
|
10
|
+
client.client_secret = hex.encode(randomBytes(32));
|
|
11
|
+
console.info('\n');
|
|
12
|
+
console.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
|
|
13
|
+
console.warn('IT IS EXCEPTIONALY UNSECURE, BUT WE GENEREATED A CLIENT SECRET FOR YOU', client.client_secret);
|
|
14
|
+
console.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
|
|
15
|
+
console.info('\n');
|
|
16
|
+
}
|
|
17
|
+
const helper = makeSecurityHelper(context);
|
|
18
|
+
const updateUri = makeUriUpdater(context, helper);
|
|
19
|
+
client.redirect_uris = client.redirect_uris?.map(updateUri) ?? [];
|
|
20
|
+
client.post_logout_redirect_uris = client.post_logout_redirect_uris?.map(updateUri) ?? [];
|
|
21
|
+
return client;
|
|
22
|
+
};
|
|
23
|
+
const makeUriUpdater = (context, helper) => (uri) => {
|
|
24
|
+
if (uri.startsWith('{{')) {
|
|
25
|
+
const [host, ...parts] = uri.split(SEP);
|
|
26
|
+
const service = context.cfg.services[host.slice(2, -2)];
|
|
27
|
+
return helper.makeUrl(service, parts.join(SEP));
|
|
28
|
+
}
|
|
29
|
+
return uri;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAErC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAE,MAAsB,EAAkB,EAAE;IACvF,IAAI,MAAM,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,WAAW,CAAC,2BAA2B,CAAC,CAAA;QACpD,CAAC;QACD,MAAM,CAAC,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAA;QAElD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClB,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;QACtD,OAAO,CAAC,IAAI,CAAC,wEAAwE,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;QAC5G,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;QACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpB,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAkB,OAAO,CAAC,CAAA;IAC3D,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACjD,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,EAAE,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACjE,MAAM,CAAC,yBAAyB,GAAG,MAAM,CAAC,yBAAyB,EAAE,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IAEzF,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAE,MAAsB,EAAE,EAAE,CAAC,CAAC,GAAW,EAAU,EAAE;IAC3F,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAEvC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACvD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAIlD,eAAO,MAAM,aAAa,YAAmB,OAAO,aAAa,OAAO,KAAG,OAAO,CAAC,aAAa,CAqC/F,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { updateClient } from './client.js';
|
|
2
|
+
import * as jose from 'jose';
|
|
3
|
+
export const combineConfig = async (context, _unsecure) => {
|
|
4
|
+
const cfg = context.cfg.oidc;
|
|
5
|
+
const configuration = {
|
|
6
|
+
...cfg.customConfiguration,
|
|
7
|
+
clients: [
|
|
8
|
+
...cfg.clients,
|
|
9
|
+
...(cfg.customConfiguration?.clients ?? [])
|
|
10
|
+
].map(client => updateClient(context, client)),
|
|
11
|
+
claims: {
|
|
12
|
+
email: ['email', 'email_verified', ...cfg.customConfiguration?.claims?.email ?? []],
|
|
13
|
+
profile: [
|
|
14
|
+
'username', 'family_name', 'given_name', 'locale', 'name', 'nickname', 'preferred_username',
|
|
15
|
+
...cfg.customConfiguration?.claims?.profile ?? []
|
|
16
|
+
],
|
|
17
|
+
...cfg.customConfiguration?.claims,
|
|
18
|
+
},
|
|
19
|
+
scopes: ['openid', 'profile', 'offline_access', ...cfg.customConfiguration?.scopes ?? []],
|
|
20
|
+
features: {
|
|
21
|
+
...cfg.customConfiguration?.features,
|
|
22
|
+
devInteractions: { enabled: false }
|
|
23
|
+
// devInteractions: {
|
|
24
|
+
// enabled: (
|
|
25
|
+
// (context.cfg.debug.all && context.cfg.debug.oidc !== false)
|
|
26
|
+
// || context.cfg.debug.oidc
|
|
27
|
+
// ) && unsecure,
|
|
28
|
+
// ...cfg.customConfiguration?.features?.devInteractions,
|
|
29
|
+
// },
|
|
30
|
+
},
|
|
31
|
+
jwks: {
|
|
32
|
+
keys: [
|
|
33
|
+
await jose.exportJWK(await jose.importPKCS8(cfg.defaultKeys.RS256.pk, 'RS256'))
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return configuration;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAgB,EAAE,SAAkB,EAA0B,EAAE;IAClG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;IAE5B,MAAM,aAAa,GAAkB;QACnC,GAAG,GAAG,CAAC,mBAAmB;QAC1B,OAAO,EAAE;YACP,GAAG,GAAG,CAAC,OAAO;YACd,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,EAAE,CAAC;SAC5C,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;YACnF,OAAO,EAAE;gBACP,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB;gBAC3F,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;aAClD;YACD,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM;SACnC;QACD,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,IAAI,EAAE,CAAC;QACzF,QAAQ,EAAE;YACR,GAAG,GAAG,CAAC,mBAAmB,EAAE,QAAQ;YACpC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACnC,qBAAqB;YACrB,eAAe;YACf,kEAAkE;YAClE,gCAAgC;YAChC,mBAAmB;YACnB,2DAA2D;YAC3D,KAAK;SACN;QACD,IAAI,EAAE;YACJ,IAAI,EAAE;gBACJ,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;aAChF;SACF;KACF,CAAA;IAED,OAAO,aAAa,CAAA;AACtB,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/server-oidc-provider",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 306 && 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
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.7.8",
|
|
24
|
+
"@types/oidc-provider": "^8.5.2",
|
|
25
|
+
"nodemon": "^3.1.7",
|
|
26
|
+
"npm-check": "^6.0.1",
|
|
27
|
+
"typescript": "^5.6.3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"fastify": "*"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@noble/hashes": "^1.5.0",
|
|
34
|
+
"@owlmeans/client-module": "^0.1.0",
|
|
35
|
+
"@owlmeans/config": "^0.1.0",
|
|
36
|
+
"@owlmeans/context": "^0.1.0",
|
|
37
|
+
"@owlmeans/oidc": "^0.1.0",
|
|
38
|
+
"@owlmeans/route": "^0.1.0",
|
|
39
|
+
"@owlmeans/server-api": "^0.1.0",
|
|
40
|
+
"@owlmeans/server-context": "^0.1.0",
|
|
41
|
+
"@scure/base": "^1.1.9",
|
|
42
|
+
"jose": "5.9.6",
|
|
43
|
+
"oidc-provider": "8.5.2"
|
|
44
|
+
},
|
|
45
|
+
"private": false,
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/consts.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Middleware } from '@owlmeans/context'
|
|
2
|
+
import { assertContext, MiddlewareStage, MiddlewareType } from '@owlmeans/context'
|
|
3
|
+
import { DEFAULT_ALIAS as WEB_ALIAS } from '@owlmeans/server-api'
|
|
4
|
+
import type { ApiServer } from '@owlmeans/server-api'
|
|
5
|
+
import { DEFAULT_ALIAS } from './consts.js'
|
|
6
|
+
import type { Config, Context, OidcProviderService } from './types.js'
|
|
7
|
+
|
|
8
|
+
export const createOidcProviderMiddleware = (web: string = WEB_ALIAS, oidc = DEFAULT_ALIAS) => {
|
|
9
|
+
const middleware: Middleware = {
|
|
10
|
+
type: MiddlewareType.Context,
|
|
11
|
+
stage: MiddlewareStage.Loading,
|
|
12
|
+
apply: async ctx => {
|
|
13
|
+
const context = assertContext<Config, Context>(ctx as unknown as Context) as Context
|
|
14
|
+
const webService = context.service<ApiServer>(web)
|
|
15
|
+
const oidcService = context.service<OidcProviderService>(oidc)
|
|
16
|
+
|
|
17
|
+
const marker = `__oidcServiceAdded-${web}-${oidc}`
|
|
18
|
+
if (!ctx.cfg.records?.find(record => record.id === marker)) {
|
|
19
|
+
await oidcService.update(webService)
|
|
20
|
+
if (ctx.cfg.records == null) {
|
|
21
|
+
ctx.cfg.records = []
|
|
22
|
+
}
|
|
23
|
+
ctx.cfg.records.push({ id: marker })
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return middleware
|
|
29
|
+
}
|
package/src/service.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { assertContext, createService } from '@owlmeans/context'
|
|
2
|
+
import { DEFAULT_ALIAS, OIDC_ACCOUNT_SERVICE } from './consts.js'
|
|
3
|
+
import { DEFAULT_PATH, INTERACTION } from '@owlmeans/oidc'
|
|
4
|
+
import type { Config, Context, OidcAccountService, OidcAdapterService, OidcProviderService } from './types.js'
|
|
5
|
+
import Provider from 'oidc-provider'
|
|
6
|
+
import type { BasicRoute } from '@owlmeans/route'
|
|
7
|
+
import type { ClientModule } from '@owlmeans/client-module'
|
|
8
|
+
import { SEP } from '@owlmeans/route'
|
|
9
|
+
import { makeSecurityHelper } from '@owlmeans/config'
|
|
10
|
+
import { combineConfig } from './utils/config.js'
|
|
11
|
+
|
|
12
|
+
let _initializedOidc: Provider | undefined = undefined
|
|
13
|
+
export const createOidcProviderService = (alias: string = DEFAULT_ALIAS): OidcProviderService => {
|
|
14
|
+
const service: OidcProviderService = createService<OidcProviderService>(alias, {
|
|
15
|
+
update: async api => {
|
|
16
|
+
const context = assertContext<Config, Context>(service.ctx as Context, alias)
|
|
17
|
+
const cfg = context.cfg.oidc
|
|
18
|
+
|
|
19
|
+
const serviceRoute = context.cfg.services[cfg.authService ?? context.cfg.service] as BasicRoute
|
|
20
|
+
const helper = makeSecurityHelper<Config, Context>(context)
|
|
21
|
+
const url = helper.makeUrl(serviceRoute, cfg.basePath ?? DEFAULT_PATH, { base: true })
|
|
22
|
+
const unsecure = context.cfg.security?.unsecure === false ? false : !url.startsWith('https')
|
|
23
|
+
|
|
24
|
+
const oidc = new Provider(url, {
|
|
25
|
+
...await combineConfig(context, unsecure),
|
|
26
|
+
|
|
27
|
+
adapter: cfg.adapterService != null
|
|
28
|
+
? name => context.service<OidcAdapterService>(cfg.adapterService!).instance(name)
|
|
29
|
+
: undefined,
|
|
30
|
+
|
|
31
|
+
findAccount: async (_, id, _token) => {
|
|
32
|
+
const accountSrv = context.service<OidcAccountService>(
|
|
33
|
+
cfg.accountService ?? OIDC_ACCOUNT_SERVICE
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return accountSrv.loadById(context, id)
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
interactions: {
|
|
40
|
+
url: async (_, interaction) => {
|
|
41
|
+
const module = context.module<ClientModule>(INTERACTION)
|
|
42
|
+
const [uri] = await module.call<string>({ params: { uid: interaction.uid } })
|
|
43
|
+
return uri
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
oidc.proxy = cfg.behindProxy ?? unsecure
|
|
49
|
+
const base = SEP + (cfg.basePath ?? DEFAULT_PATH)
|
|
50
|
+
|
|
51
|
+
await api.server.use(base, oidc.callback())
|
|
52
|
+
oidc.use(async (ctx, next) => {
|
|
53
|
+
await next()
|
|
54
|
+
const csp = ctx.response.headers['content-security-policy']
|
|
55
|
+
if (csp != null) {
|
|
56
|
+
// @TODO Make it a little bit nicer - preferably using helmet :)
|
|
57
|
+
|
|
58
|
+
ctx.response.set('Content-Security-Policy', csp.replace(/form-action 'self'/, 'form-action *'))
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
if (context.cfg.debug?.all || context.cfg.debug?.oidc) {
|
|
62
|
+
oidc.use(async (_, next) => {
|
|
63
|
+
await next()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
oidc.on('grant.error', (_, error) => {
|
|
67
|
+
console.warn('GRANT ERROR .......: ')
|
|
68
|
+
console.info(oidc.issuer, _.request.toJSON(), _.body)
|
|
69
|
+
console.error('!!!! GRANT ERROR: ', error)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
oidc.on('server_error', (ctx, error) => {
|
|
73
|
+
console.warn('SERVER ERROR .......: ', Object.getOwnPropertyNames(ctx.oidc))
|
|
74
|
+
console.info((ctx.oidc as any).grant)
|
|
75
|
+
console.error('!!!! SERVER ERROR: ', error)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_initializedOidc = service.oidc = oidc
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
instance: () => {
|
|
83
|
+
return service.oidc ?? (service.oidc = _initializedOidc!)
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
getInteraction: async id => {
|
|
87
|
+
return await service.instance().Interaction.find(id) ?? null
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
return service
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const appendOidcProviderService = <C extends Config, T extends Context<C>>(
|
|
95
|
+
ctx: T, alias: string = DEFAULT_ALIAS
|
|
96
|
+
): T => {
|
|
97
|
+
const service = createOidcProviderService(alias)
|
|
98
|
+
const context = ctx as T
|
|
99
|
+
|
|
100
|
+
context.registerService(service)
|
|
101
|
+
|
|
102
|
+
return context
|
|
103
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { InitializedService } from '@owlmeans/context'
|
|
2
|
+
import type { OidcSharedConfig } from '@owlmeans/oidc'
|
|
3
|
+
import type { ApiServer, ApiServerAppend } from '@owlmeans/server-api'
|
|
4
|
+
import type { ServerConfig, ServerContext } from '@owlmeans/server-context'
|
|
5
|
+
import type { Account, Adapter, ClientMetadata, Configuration, Interaction, Provider } from 'oidc-provider'
|
|
6
|
+
|
|
7
|
+
export interface OidcProviderService extends InitializedService {
|
|
8
|
+
oidc: Provider
|
|
9
|
+
|
|
10
|
+
update: (api: ApiServer) => Promise<void>
|
|
11
|
+
|
|
12
|
+
instance: () => Provider
|
|
13
|
+
|
|
14
|
+
getInteraction: (id: string) => Promise<Interaction | null>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface OidcConfigAppend<Extra extends OidcSharedConfig = OidcSharedConfig> {
|
|
18
|
+
oidc: OidcConfig & Extra
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface OidcConfig extends OidcSharedConfig {
|
|
22
|
+
authService?: string
|
|
23
|
+
basePath?: string
|
|
24
|
+
frontBase?: string
|
|
25
|
+
clients: ClientMetadata[]
|
|
26
|
+
customConfiguration?: Configuration
|
|
27
|
+
behindProxy?: boolean
|
|
28
|
+
defaultKeys: {
|
|
29
|
+
RS256: {
|
|
30
|
+
pk: string
|
|
31
|
+
pub?: string
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
accountService?: string
|
|
35
|
+
adapterService?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface OidcAccountService extends InitializedService {
|
|
39
|
+
loadById: <C extends Config, T extends Context<C>>(ctx: T, id: string) => Promise<Account | undefined>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface OidcAdapterService extends InitializedService {
|
|
43
|
+
instance: (name: string) => Adapter
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Config extends ServerConfig, OidcConfigAppend {
|
|
47
|
+
debug: ServerConfig["debug"] & {
|
|
48
|
+
oidc?: boolean
|
|
49
|
+
oidcServer?: boolean
|
|
50
|
+
oidcData?: boolean
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface Context<C extends Config = Config> extends ServerContext<C>
|
|
55
|
+
, ApiServerAppend { }
|