@jetbrains/ring-ui 7.0.94 → 7.0.95

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.
@@ -80,6 +80,7 @@ export interface AuthConfig extends TokenValidatorConfig {
80
80
  translations?: AuthTranslations | null | undefined;
81
81
  userParams?: RequestParams | undefined;
82
82
  waitForRedirectTimeout: number;
83
+ rpInitiatedLogout: boolean;
83
84
  }
84
85
  type AuthPayloadMap = {
85
86
  userChange: [AuthUser | undefined | void, void];
@@ -120,6 +121,7 @@ declare class Auth implements HTTPAuth {
120
121
  static DEFAULT_CONFIG: Omit<AuthConfig, "serverUri">;
121
122
  static API_PATH: string;
122
123
  static API_AUTH_PATH: string;
124
+ static API_LOGOUT_PATH: string;
123
125
  static API_PROFILE_PATH: string;
124
126
  static CLOSE_BACKEND_DOWN_MESSAGE: string;
125
127
  static CLOSE_WINDOW_MESSAGE: string;
@@ -187,7 +189,10 @@ declare class Auth implements HTTPAuth {
187
189
  private _extractErrorMessage;
188
190
  private _showBackendDownDialog;
189
191
  /**
190
- * Wipe accessToken and redirect to auth page with required authorization
192
+ * Wipe accessToken and redirect to logout endpoint.
193
+ * Uses RP-initiated logout flow (oauth2/logout) when rpInitiatedLogout config is enabled,
194
+ * falls back to oauth2/auth redirect otherwise.
195
+ * See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
191
196
  */
192
197
  logout(extraParams?: Record<string, unknown>): Promise<void>;
193
198
  private _runEmbeddedLogin;
@@ -43,12 +43,14 @@ const DEFAULT_CONFIG = {
43
43
  onBackendDown: () => () => { },
44
44
  defaultExpiresIn: DEFAULT_EXPIRES_TIMEOUT,
45
45
  waitForRedirectTimeout: DEFAULT_WAIT_FOR_REDIRECT_TIMEOUT,
46
+ rpInitiatedLogout: true,
46
47
  translations: null,
47
48
  };
48
49
  class Auth {
49
50
  static DEFAULT_CONFIG = DEFAULT_CONFIG;
50
51
  static API_PATH = 'api/rest/';
51
52
  static API_AUTH_PATH = 'oauth2/auth';
53
+ static API_LOGOUT_PATH = 'oauth2/logout';
52
54
  static API_PROFILE_PATH = 'users/me';
53
55
  static CLOSE_BACKEND_DOWN_MESSAGE = 'backend-check-succeeded';
54
56
  static CLOSE_WINDOW_MESSAGE = 'close-login-window';
@@ -106,6 +108,7 @@ class Auth {
106
108
  this._domainStorage = new AuthStorage({ messagePrefix: 'domain-message-' });
107
109
  this._requestBuilder = new AuthRequestBuilder({
108
110
  authorization: this.config.serverUri + Auth.API_PATH + Auth.API_AUTH_PATH,
111
+ logout: this.config.serverUri + Auth.API_PATH + Auth.API_LOGOUT_PATH,
109
112
  clientId,
110
113
  redirect,
111
114
  redirectUri,
@@ -633,20 +636,24 @@ class Auth {
633
636
  });
634
637
  }
635
638
  /**
636
- * Wipe accessToken and redirect to auth page with required authorization
639
+ * Wipe accessToken and redirect to logout endpoint.
640
+ * Uses RP-initiated logout flow (oauth2/logout) when rpInitiatedLogout config is enabled,
641
+ * falls back to oauth2/auth redirect otherwise.
642
+ * See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
637
643
  */
638
644
  async logout(extraParams) {
639
- const requestParams = {
640
- request_credentials: 'required',
641
- ...extraParams,
642
- };
643
645
  await this._checkBackendsStatusesIfEnabled();
644
646
  await this.listeners.trigger('logout');
645
647
  this._updateDomainUser(null);
646
648
  await this._storage?.wipeToken();
647
- const authRequest = await this._requestBuilder?.prepareAuthRequest(requestParams);
648
- if (authRequest) {
649
- this._redirectCurrentPage(authRequest.url);
649
+ const request = this.config.rpInitiatedLogout
650
+ ? await this._requestBuilder?.prepareLogoutRequest(extraParams)
651
+ : await this._requestBuilder?.prepareAuthRequest({
652
+ request_credentials: 'required',
653
+ ...extraParams,
654
+ });
655
+ if (request) {
656
+ this._redirectCurrentPage(request.url);
650
657
  }
651
658
  }
652
659
  async _runEmbeddedLogin() {
@@ -3,6 +3,7 @@ import type { AuthState } from './storage';
3
3
  import type AuthStorage from './storage';
4
4
  export interface AuthRequestBuilderConfig {
5
5
  authorization: string;
6
+ logout?: string | null | undefined;
6
7
  redirectUri?: string | null | undefined;
7
8
  requestCredentials?: string | null | undefined;
8
9
  clientId?: string | null | undefined;
@@ -38,6 +39,17 @@ export default class AuthRequestBuilder {
38
39
  url: string;
39
40
  stateId: string;
40
41
  }>;
42
+ /**
43
+ * Build a logout URL for RP-initiated logout flow.
44
+ * See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
45
+ *
46
+ * @param {object=} extraParams additional query parameters for logout request
47
+ * @return {Promise.<{url: string, stateId: string}>} logout URL with required parameters
48
+ */
49
+ prepareLogoutRequest(extraParams?: Record<string, unknown> | null | undefined): Promise<{
50
+ url: string;
51
+ stateId: string;
52
+ }>;
41
53
  /**
42
54
  * @param {string} id
43
55
  * @param {StoredState} storedState
@@ -51,6 +51,33 @@ export default class AuthRequestBuilder {
51
51
  stateId,
52
52
  };
53
53
  }
54
+ /**
55
+ * Build a logout URL for RP-initiated logout flow.
56
+ * See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
57
+ *
58
+ * @param {object=} extraParams additional query parameters for logout request
59
+ * @return {Promise.<{url: string, stateId: string}>} logout URL with required parameters
60
+ */
61
+ async prepareLogoutRequest(extraParams) {
62
+ if (!this.config.logout) {
63
+ throw new Error('Logout URL is not configured');
64
+ }
65
+ // eslint-disable-next-line no-underscore-dangle
66
+ const stateId = AuthRequestBuilder._uuid();
67
+ const logoutParams = {
68
+ client_id: this.config.clientId,
69
+ state: stateId,
70
+ ...extraParams,
71
+ };
72
+ await this._saveState(stateId, {
73
+ restoreLocation: window.location.href,
74
+ scopes: [...this.config.scopes],
75
+ });
76
+ return {
77
+ url: encodeURL(this.config.logout, logoutParams),
78
+ stateId,
79
+ };
80
+ }
54
81
  /**
55
82
  * @param {string} id
56
83
  * @param {StoredState} storedState
@@ -15,7 +15,7 @@ class Tabs extends PureComponent {
15
15
  const { selected, children } = this.props;
16
16
  const childrenArray = React.Children.toArray(children).filter(Boolean);
17
17
  const selectedIndex = childrenArray.findIndex((tab, i) => getTabId(tab, i) === selected);
18
- const actualSelectedIndex = selectedIndex === -1 ? 0 : selectedIndex;
18
+ const actualSelectedIndex = selectedIndex === -1 ? childrenArray.findIndex(tab => tab.type !== CustomItem) : selectedIndex;
19
19
  const selectedItem = childrenArray[actualSelectedIndex];
20
20
  return { selectedItem, selectedKey: getTabId(selectedItem, actualSelectedIndex) };
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.94",
3
+ "version": "7.0.95",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"