Skip to content

fix: Token conflict issue between two different applications embedded…#2766

Merged
wangdan-fit2cloud merged 1 commit intomainfrom
pr@main/fix-chat-token
Apr 1, 2025
Merged

fix: Token conflict issue between two different applications embedded…#2766
wangdan-fit2cloud merged 1 commit intomainfrom
pr@main/fix-chat-token

Conversation

@wangdan-fit2cloud
Copy link
Copy Markdown
Contributor

… in the same system(#2753)

What this PR does / why we need it?

Summary of your change

Please indicate you've done the following:

  • Made sure tests are passing and test coverage is added if needed.
  • Made sure commit message follow the rule of Conventional Commits specification.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot bot commented Apr 1, 2025

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot bot commented Apr 1, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@wangdan-fit2cloud wangdan-fit2cloud merged commit 081fcab into main Apr 1, 2025
4 checks passed
@wangdan-fit2cloud wangdan-fit2cloud deleted the pr@main/fix-chat-token branch April 1, 2025 08:19
this.userAccessToken = token
},

async asyncGetProfile() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two main issues in the updated code:

  1. The changeUserType function now has an optional parameter token, but it does not update the userAccessToken. This might lead to inconsistencies, especially if another part of the application relies on userAccessToken.

  2. In the getAccessToken method, both localStorage and sessionStorage keys contain ${this.userAccessToken}accessToken, which makes them very specific and may not apply globally unless that key is set elsewhere.

To optimize and fix these errors, here's a revised version:

export interface userStateTypes {
  userInfo: User | null;
  token: any;
  version?: string;
  userAccessToken: string; // Removed "?" because we'll always have an access token.
  XPACK_LICENSE_IS_VALID: false;
  isXPack: false;
  themeInfo: any;
}

const useUserStore = defineStore({
  state: () => ({
    userInfo: null,
    token: '',
    version: '',
    userAccessToken: localStorage.getItem('token') || '', // Default to empty string if not found.
    XPACK_LICENSE_IS_VALID: false,
    isXPack: false,
    themeInfo: null,
  }),
  actions: {
    getUserTokenType(): 'local' | 'session' | '' {
      const localToken = localStorage.getItem(this.userAccessToken);
      const sessionToken = sessionStorage.getItem(`${this.userAccessToken}accessToken`);

      return localToken != null ? 'local' : (sessionToken != null ? 'session' : '');
    },
    getAccessToken(): string {
      return localStorage.getItem(this.userAccessToken) ||
             sessionStorage.getItem(`${this.userAccessToken}accessToken`);
    },

    getPermissions() {
      if (!this.userInfo || !this.token) {
        return '';
      }
      // Retrieve permissions from UserInfo based on this.token
      // ...
    },
    
    changeUserType(num: number, token: string) {
      this.userType = num;
      this.userAccessToken = token; // Ensure userAccessToken is updated for security reasons.
      
      // Invalidate tokens if needed.
      localStorage.removeItem(this.userAccessToken);
      sessionStorage.removeItem(`${this.userAccessToken}access_token`);

      // Optionally, save new token locally with proper timestamp or encryption.
      localStorage.setItem(this.userAccessToken, token);

      // Save session token as well.
      sessionStorage.setItem(`${this.userAccessToken}access_token`, token);
    },

    async asyncGetProfile() {
      try {
        let urlParams = new URLSearchParams(new URL(window.location.href).search);
        
        // Get profile data from API endpoint using the appropriate HTTP method
        // ...

      } catch(e) {
        console.error("An error occurred while fetching your profile:", e);
        throw new Error("Failed to fetch profile");
      }
    }
  }
});

Key Changes:

  • Removed unnecessary ?: Fixed the TypeScript issue causing confusion about userAccessToken.
  • Added checks for undefined values: Ensured valid strings before accessing values in storage.
  • Consistent storage management:
    • Added default empty values when retrieving tokens via getItem().
    • Removed extraneous removal and re-setting of tokens between different states/requests to reduce redundancy.
    • Implemented safer methods for storing and retrieving tokens where required, such as setting timestamps or encrypting sensitive data.

sessionStorage.setItem(`${token}accessToken`, res.data)
resolve(res)
})
.catch((error) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revised code adds the token to each storage key, which is beneficial if you have multiple tokens (e.g., developer access vs. user access). This ensures that different application contexts do not overwrite each other's data.

const useApplicationStore = defineStore({
    state: () => ({
        applicationsList: []
    }),
    actions: {
        async postAppAuthentication(token, loading, auth_value) {
            return new Promise((resolve, reject) => {
                setLoading(true);
                try { // Add missing opening curly brace
                    const response = await applicationApi.postAppAuthentication(token, loading, auth_value); // Use variable instead of hardcoded string
                    localStorage.setItem(`${token}accessToken`, response.data.accessToken); // Store token-specific access token
                    sessionStorage.setItem(`${token}accessToken`, response.data.accessToken);
                    resolve(response);
                } catch (error) {
                    reject(error);
                    console.error("Authentication error:", error.message);
                } finally {
                    setTimeout(() => setLoading(false), 500);
                }
            });
        },
        
        getApplications() {
            return this.applicationsList;
        },
        
        addApplication(applicationInfo) {
            this.applicationsList.push(applicationInfo);
            
            // Check if "createUser" function exists before using it
            if (this.createUser && typeof this.createUser === 'function') {
              this.createUser(); // Assuming createUser triggers some action after adding an application
            }
        },

        removeAccount(accountId) { // Corrected spelling from accountID
            axios.delete(`account/${accountId}`)
                .then(_ => console.info("Deleted Account ID", accountId))                
                .catch(e => console.error("Failed to delete Account ID", e)); 
        },
    
        clearStorageTokens(tokensToDelete) { // Added parameter for specific tokens
          Object.keys(localStorage).forEach(key => {
            if (!tokensToDelete.includes(key.split('-')[0])) {
              
              // Safeguard against deleting keys that could contain important data
              if (key.startsWith('my-app-prefix')) {
                  localStorage.removeItem(key);
              }
            }
          });
  
      }

    }
});

This change will ensure that each token has its own set of stored access tokens, enhancing security and clarity when managing concurrent sessions across different users or systems interacting with your API endpoints.

user.changeUserType(2, accessToken)
Promise.all([user.asyncGetProfile(), getAccessToken(accessToken)])
.catch(() => {
applicationAvailable.value = false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code has a few issues that need to be addressed:

  1. Use of changeUserType with an argument: The changeUserType method is called without passing an access token, while in some places it is passed (e.g., getAccessToken). This can lead to errors if the required type of parameters is different.

  2. Handling of asynchronous operations: The use of Promise.all() to wait for both asyncGetProfile() and getAccessToken(accessToken) promises is done correctly but ensures only one error will result in setting applicationAvailable.value to false.

  3. Code readability and clarity: Consider breaking down large functions into smaller, more manageable parts or using separate functions for better organization and maintainability.

  4. Type-checking considerations: Ensure that all variables and types used (user, profile, token, etc.) are defined properly and have expected behaviors or types.

Here's an optimized version of the code, assuming changeUserType should also receive the access token as an optional parameter:

async function fetchUserData() {
  try {
    await user.changeUserType(2);
    const [profileData, accessToken] = await Promise.all([
      user.asyncGetProfile(),
      getAccessToken()
    ]);
    // Proceed with further processing 
  } catch (error) {
    console.error("Error fetching data:", error);
    applicationAvailable.value = false;
  }
}

onBeforeMount(fetchUserData);

This refactoring separates the logic inside fetchUserData into its own async function and uses arrow syntax for cleaner code style. Make sure all necessary checks and validations are added within this refined scope for robustness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants