summaryrefslogtreecommitdiff
path: root/bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js
diff options
context:
space:
mode:
Diffstat (limited to 'bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js')
-rw-r--r--bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js b/bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js
new file mode 100644
index 0000000..6eea88c
--- /dev/null
+++ b/bot/node_modules/matrix-js-sdk/lib/oidc/authorize.js
@@ -0,0 +1,248 @@
+"use strict";
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.generateOidcAuthorizationUrl = exports.generateAuthorizationUrl = exports.generateAuthorizationParams = exports.completeAuthorizationCodeGrant = void 0;
+var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
+var _oidcClientTs = require("oidc-client-ts");
+var _crypto = require("../crypto/crypto");
+var _logger = require("../logger");
+var _randomstring = require("../randomstring");
+var _error = require("./error");
+var _validate = require("./validate");
+function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
+function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /*
+ Copyright 2023 The Matrix.org Foundation C.I.C.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+// reexport for backwards compatibility
+
+/**
+ * Authorization parameters which are used in the authentication request of an OIDC auth code flow.
+ *
+ * See https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters.
+ */
+
+/**
+ * @experimental
+ * Generate the scope used in authorization request with OIDC OP
+ * @returns scope
+ */
+const generateScope = () => {
+ const deviceId = (0, _randomstring.randomString)(10);
+ return `openid urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:${deviceId}`;
+};
+
+// https://www.rfc-editor.org/rfc/rfc7636
+const generateCodeChallenge = async codeVerifier => {
+ if (!_crypto.subtleCrypto) {
+ // @TODO(kerrya) should this be allowed? configurable?
+ _logger.logger.warn("A secure context is required to generate code challenge. Using plain text code challenge");
+ return codeVerifier;
+ }
+ const utf8 = new _crypto.TextEncoder().encode(codeVerifier);
+ const digest = await _crypto.subtleCrypto.digest("SHA-256", utf8);
+ return btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+};
+
+/**
+ * Generate authorization params to pass to {@link generateAuthorizationUrl}.
+ *
+ * Used as part of an authorization code OIDC flow: see https://openid.net/specs/openid-connect-basic-1_0.html#CodeFlow.
+ *
+ * @param redirectUri - absolute url for OP to redirect to after authorization
+ * @returns AuthorizationParams
+ */
+const generateAuthorizationParams = ({
+ redirectUri
+}) => ({
+ scope: generateScope(),
+ redirectUri,
+ state: (0, _randomstring.randomString)(8),
+ nonce: (0, _randomstring.randomString)(8),
+ codeVerifier: (0, _randomstring.randomString)(64) // https://tools.ietf.org/html/rfc7636#section-4.1 length needs to be 43-128 characters
+});
+
+/**
+ * @deprecated use generateOidcAuthorizationUrl
+ * Generate a URL to attempt authorization with the OP
+ * See https://openid.net/specs/openid-connect-basic-1_0.html#CodeRequest
+ * @param authorizationUrl - endpoint to attempt authorization with the OP
+ * @param clientId - id of this client as registered with the OP
+ * @param authorizationParams - params to be used in the url
+ * @returns a Promise with the url as a string
+ */
+exports.generateAuthorizationParams = generateAuthorizationParams;
+const generateAuthorizationUrl = async (authorizationUrl, clientId, {
+ scope,
+ redirectUri,
+ state,
+ nonce,
+ codeVerifier
+}) => {
+ const url = new URL(authorizationUrl);
+ url.searchParams.append("response_mode", "query");
+ url.searchParams.append("response_type", "code");
+ url.searchParams.append("redirect_uri", redirectUri);
+ url.searchParams.append("client_id", clientId);
+ url.searchParams.append("state", state);
+ url.searchParams.append("scope", scope);
+ url.searchParams.append("nonce", nonce);
+ url.searchParams.append("code_challenge_method", "S256");
+ url.searchParams.append("code_challenge", await generateCodeChallenge(codeVerifier));
+ return url.toString();
+};
+
+/**
+ * @experimental
+ * Generate a URL to attempt authorization with the OP
+ * See https://openid.net/specs/openid-connect-basic-1_0.html#CodeRequest
+ * @param oidcClientSettings - oidc configuration
+ * @param homeserverName - used as state
+ * @returns a Promise with the url as a string
+ */
+exports.generateAuthorizationUrl = generateAuthorizationUrl;
+const generateOidcAuthorizationUrl = async ({
+ metadata,
+ redirectUri,
+ clientId,
+ homeserverUrl,
+ identityServerUrl,
+ nonce
+}) => {
+ const scope = await generateScope();
+ const oidcClient = new _oidcClientTs.OidcClient(_objectSpread(_objectSpread({}, metadata), {}, {
+ client_id: clientId,
+ redirect_uri: redirectUri,
+ authority: metadata.issuer,
+ response_mode: "query",
+ response_type: "code",
+ scope,
+ stateStore: new _oidcClientTs.WebStorageStateStore({
+ prefix: "mx_oidc_",
+ store: window.sessionStorage
+ })
+ }));
+ const userState = {
+ homeserverUrl,
+ nonce,
+ identityServerUrl
+ };
+ const request = await oidcClient.createSigninRequest({
+ state: userState,
+ nonce
+ });
+ return request.url;
+};
+
+/**
+ * Normalize token_type to use capital case to make consuming the token response easier
+ * token_type is case insensitive, and it is spec-compliant for OPs to return token_type: "bearer"
+ * Later, when used in auth headers it is case sensitive and must be Bearer
+ * See: https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.4
+ *
+ * @param response - validated token response
+ * @returns response with token_type set to 'Bearer'
+ */
+exports.generateOidcAuthorizationUrl = generateOidcAuthorizationUrl;
+const normalizeBearerTokenResponseTokenType = response => ({
+ id_token: response.id_token,
+ scope: response.scope,
+ expires_at: response.expires_at,
+ refresh_token: response.refresh_token,
+ access_token: response.access_token,
+ token_type: "Bearer"
+});
+
+/**
+ * @experimental
+ * Attempt to exchange authorization code for bearer token.
+ *
+ * Takes the authorization code returned by the OpenID Provider via the authorization URL, and makes a
+ * request to the Token Endpoint, to obtain the access token, refresh token, etc.
+ *
+ * @param code - authorization code as returned by OP during authorization
+ * @param storedAuthorizationParams - stored params from start of oidc login flow
+ * @returns valid bearer token response
+ * @throws when request fails, or returned token response is invalid
+ */
+const completeAuthorizationCodeGrant = async (code, state) => {
+ /**
+ * Element Web strips and changes the url on starting the app
+ * Use the code and state from query params to rebuild a url
+ * so that oidc-client can parse it
+ */
+ const reconstructedUrl = new URL(window.location.origin);
+ reconstructedUrl.searchParams.append("code", code);
+ reconstructedUrl.searchParams.append("state", state);
+
+ // set oidc-client to use our logger
+ _oidcClientTs.Log.setLogger(_logger.logger);
+ try {
+ const response = new _oidcClientTs.SigninResponse(reconstructedUrl.searchParams);
+ const stateStore = new _oidcClientTs.WebStorageStateStore({
+ prefix: "mx_oidc_",
+ store: window.sessionStorage
+ });
+
+ // retrieve the state we put in storage at the start of oidc auth flow
+ const stateString = await stateStore.get(response.state);
+ if (!stateString) {
+ throw new Error(_error.OidcError.MissingOrInvalidStoredState);
+ }
+
+ // hydrate the sign in state and create a client
+ // the stored sign in state includes oidc configuration we set at the start of the oidc login flow
+ const signInState = _oidcClientTs.SigninState.fromStorageString(stateString);
+ const client = new _oidcClientTs.OidcClient(_objectSpread(_objectSpread({}, signInState), {}, {
+ stateStore
+ }));
+
+ // validate the code and state, and attempt to swap the code for tokens
+ const signinResponse = await client.processSigninResponse(reconstructedUrl.href);
+
+ // extra values we stored at the start of the login flow
+ // used to complete login in the client
+ const userState = signinResponse.userState;
+ (0, _validate.validateStoredUserState)(userState);
+
+ // throws when response is invalid
+ (0, _validate.validateBearerTokenResponse)(signinResponse);
+ // throws when token is invalid
+ (0, _validate.validateIdToken)(signinResponse.id_token, client.settings.authority, client.settings.client_id, userState.nonce);
+ const normalizedTokenResponse = normalizeBearerTokenResponseTokenType(signinResponse);
+ return {
+ oidcClientSettings: {
+ clientId: client.settings.client_id,
+ issuer: client.settings.authority
+ },
+ tokenResponse: normalizedTokenResponse,
+ homeserverUrl: userState.homeserverUrl,
+ identityServerUrl: userState.identityServerUrl
+ };
+ } catch (error) {
+ _logger.logger.error("Oidc login failed", error);
+ const errorType = error.message;
+
+ // rethrow errors that we recognise
+ if (Object.values(_error.OidcError).includes(errorType)) {
+ throw error;
+ }
+ throw new Error(_error.OidcError.CodeExchangeFailed);
+ }
+};
+exports.completeAuthorizationCodeGrant = completeAuthorizationCodeGrant;
+//# sourceMappingURL=authorize.js.map \ No newline at end of file