1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | import { injectable, inject } from "tsyringe";
|
32 | import {
|
33 | IClientRegistrar,
|
34 | IIssuerConfig,
|
35 | IIssuerConfigFetcher,
|
36 | ILoginOptions,
|
37 | ILoginHandler,
|
38 | IOidcHandler,
|
39 | IOidcOptions,
|
40 | IStorageUtility,
|
41 | ConfigurationError,
|
42 | LoginResult,
|
43 | } from "@inrupt/solid-client-authn-core";
|
44 |
|
45 | import { IClient } from "@inrupt/oidc-client-ext";
|
46 |
|
47 | function hasIssuer(
|
48 | options: ILoginOptions
|
49 | ): options is ILoginOptions & { oidcIssuer: string } {
|
50 | return typeof options.oidcIssuer === "string";
|
51 | }
|
52 |
|
53 | function hasRedirectUrl(
|
54 | options: ILoginOptions
|
55 | ): options is ILoginOptions & { redirectUrl: string } {
|
56 | return typeof options.redirectUrl === "string";
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | @injectable()
|
63 | export default class OidcLoginHandler implements ILoginHandler {
|
64 | constructor(
|
65 | @inject("storageUtility") private storageUtility: IStorageUtility,
|
66 | @inject("oidcHandler") private oidcHandler: IOidcHandler,
|
67 | @inject("issuerConfigFetcher")
|
68 | private issuerConfigFetcher: IIssuerConfigFetcher,
|
69 | @inject("clientRegistrar") private clientRegistrar: IClientRegistrar
|
70 | ) {}
|
71 |
|
72 | async canHandle(options: ILoginOptions): Promise<boolean> {
|
73 | return hasIssuer(options) && hasRedirectUrl(options);
|
74 | }
|
75 |
|
76 | async handle(options: ILoginOptions): Promise<LoginResult> {
|
77 | if (!hasIssuer(options)) {
|
78 | throw new ConfigurationError(
|
79 | `OidcLoginHandler requires an OIDC issuer: missing property 'oidcIssuer' in ${JSON.stringify(
|
80 | options
|
81 | )}`
|
82 | );
|
83 | }
|
84 | if (!hasRedirectUrl(options)) {
|
85 | throw new ConfigurationError(
|
86 | `OidcLoginHandler requires a redirect URL: missing property 'redirectUrl' in ${JSON.stringify(
|
87 | options
|
88 | )}`
|
89 | );
|
90 | }
|
91 |
|
92 |
|
93 | const issuerConfig: IIssuerConfig = await this.issuerConfigFetcher.fetchConfig(
|
94 | options.oidcIssuer
|
95 | );
|
96 |
|
97 | let dynamicClientRegistration: IClient;
|
98 | if (options.clientId) {
|
99 | dynamicClientRegistration = {
|
100 | clientId: options.clientId,
|
101 | clientSecret: options.clientSecret,
|
102 | clientName: options.clientName,
|
103 | };
|
104 | } else {
|
105 | const clientId = await this.storageUtility.getForUser(
|
106 | "clientApplicationRegistrationInfo",
|
107 | "clientId"
|
108 | );
|
109 |
|
110 | if (clientId) {
|
111 | dynamicClientRegistration = {
|
112 | clientId,
|
113 | clientSecret: await this.storageUtility.getForUser(
|
114 | "clientApplicationRegistrationInfo",
|
115 | "clientSecret"
|
116 | ),
|
117 | clientName: options.clientName,
|
118 | };
|
119 | } else {
|
120 | dynamicClientRegistration = await this.clientRegistrar.getClient(
|
121 | {
|
122 | sessionId: options.sessionId,
|
123 | clientName: options.clientName,
|
124 | redirectUrl: options.redirectUrl,
|
125 | },
|
126 | issuerConfig
|
127 | );
|
128 |
|
129 | await this.storageUtility.setForUser(
|
130 | "clientApplicationRegistrationInfo",
|
131 | {
|
132 | clientId: dynamicClientRegistration.clientId,
|
133 | clientSecret: dynamicClientRegistration.clientSecret as string,
|
134 | }
|
135 | );
|
136 | }
|
137 | }
|
138 |
|
139 |
|
140 | const OidcOptions: IOidcOptions = {
|
141 | issuer: options.oidcIssuer,
|
142 |
|
143 | dpop: options.tokenType.toLowerCase() === "dpop",
|
144 | redirectUrl: options.redirectUrl,
|
145 | issuerConfiguration: issuerConfig,
|
146 | client: dynamicClientRegistration,
|
147 | sessionId: options.sessionId,
|
148 | handleRedirect: options.handleRedirect,
|
149 | };
|
150 |
|
151 |
|
152 | return this.oidcHandler.handle(OidcOptions);
|
153 | }
|
154 | }
|