LoginSignup
0
0

Vue 3のComposition APIとTypeScript用 msal-browserプラグイン

Last updated at Posted at 2024-02-05

dev.toにも投稿したのですが、こちらにも。

Microsoft Authentication Library (MSAL)は、簡単に言えばEntra ID(Azure AD)やAzure AD B2Cへ接続して認証認可をするためのライブラリーです。いろんな言語環境に対応していますが、その中の一つである msal-browser はSPAで認証認可してIDトークンやアクセストークンを得るためのもの。

MSのサンプルコードにはReactやAngular、Vanilla.jsなどで使う例があるのですが、Vueはなかったので、MSAL.jsを使ってウェブフロントエンドだけでAzureAD認証する | フューチャー技術ブログ を参考にして書いてみた。

ソース

src/plugins/msal.ts
import { ref, computed, ComputedRef, inject } from "vue";
import { type Plugin, type Ref } from 'vue';

import {
  Configuration,
  PublicClientApplication,
  LogLevel,
  AuthenticationResult,
} from "@azure/msal-browser";

export type Msal = {
  isAuthenticated: ComputedRef<boolean>,
  result: Ref<AuthenticationResult | null>,
  login: () => Promise<void>,
  logout: () => Promise<void>,
};

export const createMsal = async () => {
  const config: Configuration = {
    auth: {
      clientId: import.meta.env.VITE_AUTH_CLIENTID,
      authority: import.meta.env.VITE_AUTH_AUTHORITY,
      knownAuthorities: [import.meta.env.VITE_AUTH_TENANT_DOMAIN],
      redirectUri: import.meta.env.VITE_AUTH_REDIRECT_URI,
    },
    cache: {
      cacheLocation: "localStorage",
      storeAuthStateInCookie: false,
    },
    system: {
      loggerOptions: {
        loggerCallback: (level, message, containsPii) => {
          if (containsPii) {
            return;
          }
          switch (level) {
            case LogLevel.Error:
              console.error(message);
              return;
            case LogLevel.Warning:
              console.warn(message);
              return;
            case LogLevel.Info:
              console.info(message);
              return;
            case LogLevel.Verbose:
              console.debug(message);
              return;
          }
        },
      },
    },
  };

  const scopes = (import.meta.env.VITE_AUTH_SCOPES as string).split(' ').filter(Boolean);
  const client = new PublicClientApplication(config);
  await client.initialize();
  const result = ref(await client.handleRedirectPromise());
  const isAuthenticated = computed(() => result.value != null);
  const login = async () => {
    if (client.getAllAccounts().length > 0) {
      client.setActiveAccount(client.getAllAccounts()[0]);
      result.value = await client.acquireTokenSilent({
        redirectUri: config.auth.redirectUri,
        scopes: scopes
      });
    } else {
      await client.acquireTokenRedirect({
        redirectStartPage: location.href,
        redirectUri: config.auth.redirectUri,
        scopes: scopes
      });
    }
  };
  const logout = async () => {
    await client.logoutRedirect();
  }

  const msalPlugin: Plugin = {
    install(app) {
      app.provide<Msal>("msal", { isAuthenticated, result, login, logout });
    }
  }
  return msalPlugin;
};

export const useMsal = () => {
  return inject<Msal>("msal")!;
}

使い方

awaitしなくても大丈夫な気もするけど、いちおう。

src/main.ts
import { createApp } from "vue";
import { createMsal } from "@/plugins/msal";

import App from "@/App.vue";

const app = createApp(App);
app.use(pinia);

(async () => {
  const msal = await createMsal();
  app.use(msal);
  app.mount("#app");
})();
src/component/HelloWorld.vue
<script setup lang="ts">
import { useMsal } from '@/plugins/msal';

defineProps<{ msg: string }>()

const { isAuthenticated, result, login, logout } = useMsal();
console.log(result.value?.idToken);
console.log(result.value?.accessToken);
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <template v-if="isAuthenticated">
      <p>
        Hello, {{ result?.account.name }} !
      </p>
      <button type="button" @click="logout">Logout</button>
    </template>
    <template v-else>
      <button type="button" @click="login">Login</button>
    </template>
  </div>
</template>

<style scoped>
</style>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0