使用 vue app for azure 时出现 uninitialized_public_client_application 错误

问题描述 投票:0回答:1

我正在尝试从 Azure 获取访问令牌,但在运行代码时遇到错误:

BrowserAuthError:uninitialized_public_client_application:在尝试调用任何其他 MSAL API 之前,必须调用并等待初始化函数。

有人可以帮我解决这个问题吗?

这是我的 msalConfig.ts

export const msalConfig = {
  auth: {
    clientId: import.meta.env.VITE_APP_CLIENT_ID,
    authority: `https://login.microsoftonline.com/${import.meta.env.VITE_APP_TENANT_ID}`,
    redirectUri: "https://localhost/spa/sign-in",
    postLogoutUrl: "https://localhost",
  },
  cache: {
    cacheLocation: "localStorage",
    storeAuthStateInCookie: false,
  },
};

export const loginRequest = {
  scopes: ["User.Read"],
};

我的initializeMsal.ts代码:

import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig } from "@/config/msalConfig";

let msalInstance: PublicClientApplication | null = null;

export const initializeMsal = async (): Promise<PublicClientApplication> => {
  if (!msalInstance) {
    msalInstance = new PublicClientApplication(msalConfig);
  }
  return msalInstance;
};

我的main.ts:

import { createApp } from 'vue';
import App from './App.vue';
import { initializeMsal } from './msalAuthService';

const initApp = async () => {
  try {
    await initializeMsal();
    createApp(App).mount('#app');
  } catch (error) {
    console.error('Failed to initialize MSAL', error);
  }
};

initApp();

我的 msalAuthService.ts:

import type { AccountInfo, AuthenticationResult } from "@azure/msal-browser";
import { loginRequest } from "@/config/msalConfig";
import { initializeMsal } from "@/config/initializeMsal";

export const login = async (): Promise<AuthenticationResult | null> => {
  try {
    const msalInstance = await initializeMsal();
    return await msalInstance.loginPopup(loginRequest);
  } catch (error) {
    console.error(error);
    return null;
  }
};

export const logout = async (): Promise<void> => {
  try {
    const msalInstance = await initializeMsal();
    await msalInstance.logout();
  } catch (error) {
    console.error(error);
  }
};

export const getAccount = async (): Promise<AccountInfo | null> => {
  const msalInstance = await initializeMsal();
  const accounts = msalInstance.getAllAccounts();
  return accounts.length > 0 ? accounts[0] : null;
};

export const acquireToken = async (): Promise<string | null> => {
  const msalInstance = await initializeMsal();
  try {
    const account = getAccount();
    if (!account) {
      throw new Error('No signed in account found.');
    }
    const response = await msalInstance.acquireTokenSilent({
      ...loginRequest,
      account,
    });
    return response.accessToken;
  } catch (error) {
    console.error(error);
    return null;
  }
};

azure vue.js azure-ad-msal
1个回答
0
投票

BrowserAuthError:uninitialized_public_client_application:在尝试调用任何其他 MSAL API 之前,必须调用并等待初始化函数。

我的环境中也遇到了上述错误。我尝试了一个示例 Vue.js 项目作为前端,JavaScript 作为后端来获取访问令牌。

代码:

vueapp/src/components/AuthButton.vue:

<template>
  <div>
    <button @click="redirectToAuth">Login with Microsoft</button>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'AuthButton',
  methods: {
    redirectToAuth() {
      const clientId = process.env.VUE_APP_CLIENT_ID;
      const redirectUri = 'http://localhost:3000/callback';
      const authUrl = `https://login.microsoftonline.com/${process.env.VUE_APP_TENANT_ID}/oauth2/v2.0/authorize`;
      const scope = encodeURIComponent('openid profile offline_access');
      const authorizationUrl = `${authUrl}?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}`;
      window.location.href = authorizationUrl;
    }
  }
});
</script>

vueapp/src/views/HomePage.vue

<template>
  <div>
    <h1>Welcome to the Vue Azure Auth App</h1>
    <AuthButton />
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import AuthButton from '@/components/AuthButton.vue';
export default defineComponent({
  name: 'HomePage',
  components: {
    AuthButton
  }
});
</script>

vueapp/src/views/OAuthCallback.vue

<template>
  <div>
    <p v-if="error">{{ error }}</p>
    <p v-if="accessToken">Access Token: {{ accessToken }}</p>
  </div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import axios from 'axios';
const accessToken = ref('');
const error = ref('');
onMounted(async () => {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  console.log('Authorization Code:', code);
  if (code) {
    try {
      const response = await axios.post('/api/token', { code });
      console.log('Token Response:', response.data);
      accessToken.value = response.data.access_token;
    } catch (err) {
      console.error('Error exchanging authorization code for access token:', err);
      error.value = 'Failed to obtain access token';
    }
  }
});
</script>
<style scoped>
</style>

vueapp/src/main.ts:

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');

vueapp/src/router.ts:

import { createRouter, createWebHistory } from 'vue-router';
import HomePage from '@/views/HomePage.vue';
import OAuthCallback from '@/views/OAuthCallback.vue';

const routes = [
  { path: '/', component: HomePage },
  { path: '/callback', component: OAuthCallback },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

vueapp/src/App.vue:

<template>
  <router-view />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'App'
});
</script>
<style scoped>
</style>

vueapp/vue.config.js :

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3001',
        changeOrigin: true
      }
    }
  }
};

vueapp/.env:

VUE_APP_CLIENT_ID='<CLIENT_ID>'
VUE_APP_TENANT_ID='<TENANT_ID>'

后端/server.js :

const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
require('dotenv').config();
const app = express();
const port = 3001;
app.use(bodyParser.json());
app.post('/api/token', async (req, res) => {
  const { code } = req.body;
  const tokenUrl = `https://login.microsoftonline.com/${process.env.VUE_APP_TENANT_ID}/oauth2/v2.0/token`;
  const params = new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: process.env.VUE_APP_CLIENT_ID,
    client_secret: process.env.VUE_APP_CLIENT_SECRET,
    code: code,
    redirect_uri: 'http://localhost:3000/callback',
  });
  try {
    const response = await axios.post(tokenUrl, params, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    });
    res.json(response.data);
  } catch (error) {
    console.error('Error occured on getting access token:', error);
    res.status(500).send('Internal Server Error');
  }
});
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

后端/.env:

VUE_APP_CLIENT_ID='<CLIENT_ID>'
VUE_APP_CLIENT_SECRET='<CLIENT_SECRET>'
VUE_APP_TENANT_ID='<TENANT_ID>'

后端/package.json:

{
    "name": "backend",
    "version": "1.0.0",
    "main": "server.js",
    "dependencies": {
      "axios": "^0.21.1",
      "body-parser": "^1.19.0",
      "dotenv": "^8.2.0",
      "express": "^4.17.1"
    }
  } 

我在 Azure AD 应用程序的 Authentication 中添加了以下 URL 作为 单页应用程序Web

http://localhost:3000

enter image description here

http://localhost:3000/callback

enter image description here

输出:

我运行后端项目如下:

enter image description here

然后,我运行了Vue.js项目,如下所示:

enter image description here

浏览器输出:

我在浏览器中获得了以下内容以及 Vue.js 输出 URL,然后单击 用 Microsoft 登录

enter image description here

我获得的访问令牌如下所示:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.