1 | /*
|
2 | * Copyright 2020 Inrupt Inc.
|
3 | *
|
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 | * of this software and associated documentation files (the "Software"), to deal in
|
6 | * the Software without restriction, including without limitation the rights to use,
|
7 | * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
8 | * Software, and to permit persons to whom the Software is furnished to do so,
|
9 | * subject to the following conditions:
|
10 | *
|
11 | * The above copyright notice and this permission notice shall be included in
|
12 | * all copies or substantial portions of the Software.
|
13 | *
|
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
15 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
16 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
17 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
18 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
19 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20 | */
|
21 |
|
22 | /**
|
23 | * @hidden
|
24 | * @packageDocumentation
|
25 | */
|
26 |
|
27 | import { injectable, inject } from "tsyringe";
|
28 | import {
|
29 | ILoginInputOptions,
|
30 | ILoginHandler,
|
31 | ILogoutHandler,
|
32 | IRedirectHandler,
|
33 | ISessionInfo,
|
34 | ISessionInfoManager,
|
35 | } from "@inrupt/solid-client-authn-core";
|
36 | import { removeOidcQueryParam } from "@inrupt/oidc-client-ext";
|
37 |
|
38 | /**
|
39 | * @hidden
|
40 | */
|
41 | ()
|
42 | export default class ClientAuthentication {
|
43 | constructor(
|
44 | "loginHandler") private loginHandler: ILoginHandler,
( |
45 | "redirectHandler") private redirectHandler: IRedirectHandler,
( |
46 | "logoutHandler") private logoutHandler: ILogoutHandler,
( |
47 | "sessionInfoManager")
( |
48 | private sessionInfoManager: ISessionInfoManager
|
49 | ) {}
|
50 |
|
51 | // Define these functions as properties so that they don't get accidentally re-bound.
|
52 | // Isn't Javascript fun?
|
53 | login = async (
|
54 | sessionId: string,
|
55 | options: ILoginInputOptions
|
56 | ): Promise<void> => {
|
57 | // In order to get a clean start, make sure that the session is logged out
|
58 | // on login.
|
59 | // But we may want to preserve our client application info, particularly if
|
60 | // we used Dynamic Client Registration to register (since we don't
|
61 | // necessarily want the user to have to register this app each time they
|
62 | // login).
|
63 | await this.sessionInfoManager.clear(sessionId);
|
64 |
|
65 | // In the case of the user hitting the 'back' button in their browser, they
|
66 | // could return to a previous redirect URL that contains OIDC params that
|
67 | // are now longer valid - so just to be safe, strip relevant params now.
|
68 | const redirectUrl = removeOidcQueryParam(
|
69 | options.redirectUrl ?? window.location.href
|
70 | );
|
71 |
|
72 | await this.loginHandler.handle({
|
73 | sessionId,
|
74 | oidcIssuer: options.oidcIssuer,
|
75 | redirectUrl,
|
76 | clientId: options.clientId,
|
77 | clientSecret: options.clientSecret,
|
78 | clientName: options.clientName ?? options.clientId,
|
79 | popUp: options.popUp || false,
|
80 | handleRedirect: options.handleRedirect,
|
81 | // Defaults to DPoP
|
82 | tokenType: options.tokenType ?? "DPoP",
|
83 | });
|
84 | };
|
85 |
|
86 | // By default, our fetch() resolves to the environment fetch() function.
|
87 | fetch = window.fetch;
|
88 |
|
89 | logout = async (sessionId: string): Promise<void> => {
|
90 | await this.logoutHandler.handle(sessionId);
|
91 |
|
92 | // Restore our fetch() function back to the environment fetch(), effectively
|
93 | // leaving us with un-authenticated fetches from now on.
|
94 | this.fetch = window.fetch;
|
95 | };
|
96 |
|
97 | getSessionInfo = async (
|
98 | sessionId: string
|
99 | ): Promise<ISessionInfo | undefined> => {
|
100 | // TODO complete
|
101 | return this.sessionInfoManager.get(sessionId);
|
102 | };
|
103 |
|
104 | getAllSessionInfo = async (): Promise<ISessionInfo[]> => {
|
105 | return this.sessionInfoManager.getAll();
|
106 | };
|
107 |
|
108 | handleIncomingRedirect = async (
|
109 | url: string
|
110 | ): Promise<ISessionInfo | undefined> => {
|
111 | const redirectInfo = await this.redirectHandler.handle(url);
|
112 |
|
113 | this.fetch = redirectInfo.fetch;
|
114 |
|
115 | const cleanedUpUrl = new URL(url);
|
116 | cleanedUpUrl.searchParams.delete("state");
|
117 | // For auth code flow
|
118 | cleanedUpUrl.searchParams.delete("code");
|
119 | // For implicit flow
|
120 | cleanedUpUrl.searchParams.delete("id_token");
|
121 | cleanedUpUrl.searchParams.delete("access_token");
|
122 |
|
123 | // Remove OAuth-specific query params (since the login flow finishes with the
|
124 | // browser being redirected back with OAuth2 query params (e.g. for 'code'
|
125 | // and 'state'), and so if the user simply refreshes this page our
|
126 | // authentication library will be called again with what are now invalid
|
127 | // query parameters!).
|
128 | window.history.replaceState(null, "", cleanedUpUrl.toString());
|
129 |
|
130 | return {
|
131 | isLoggedIn: redirectInfo.isLoggedIn,
|
132 | webId: redirectInfo.webId,
|
133 | sessionId: redirectInfo.sessionId,
|
134 | };
|
135 | };
|
136 | }
|