Skip to main content

Add authentication to your Vue.js app

Open in Cursor

This guide walks you through adding authentication and access control to a Vue.js application using the @lumoauth/sdk with Vue Composition API.


Before you start

You need:

  • A LumoAuth account with a configured tenant (sign up)
  • A registered OAuth application with a redirect URI (e.g., http://localhost:5173/auth/callback)
  • Vue 3 + Vite

Install the SDK

npm install @lumoauth/sdk

Environment variables

VITE_LUMOAUTH_DOMAIN=https://app.lumoauth.dev
VITE_LUMOAUTH_TENANT=YOUR_TENANT_SLUG
VITE_LUMOAUTH_CLIENT_ID=YOUR_CLIENT_ID

Set up the composable

Create src/composables/useLumoAuth.ts:

import { inject, provide, type InjectionKey } from 'vue';
import { LumoAuth, AuthModule } from '@lumoauth/sdk';

const LumoAuthKey: InjectionKey<LumoAuth> = Symbol('LumoAuth');
const AuthModuleKey: InjectionKey<AuthModule> = Symbol('AuthModule');

export function provideLumoAuth() {
const domain = import.meta.env.VITE_LUMOAUTH_DOMAIN as string;
const tenantSlug = import.meta.env.VITE_LUMOAUTH_TENANT as string;
const clientId = import.meta.env.VITE_LUMOAUTH_CLIENT_ID as string;

const authModule = new AuthModule({ baseUrl: domain, tenantSlug, clientId });
const client = new LumoAuth({
baseUrl: domain,
tenantSlug,
clientId,
token: () => sessionStorage.getItem('lumoauth_access_token') || '',
});

provide(LumoAuthKey, client);
provide(AuthModuleKey, authModule);
return { client, authModule };
}

export function useLumoAuth() {
const client = inject(LumoAuthKey);
if (!client) throw new Error('Call provideLumoAuth() in a parent component');
return client;
}

export function useAuthModule() {
const auth = inject(AuthModuleKey);
if (!auth) throw new Error('Call provideLumoAuth() in a parent component');
return auth;
}

Call provideLumoAuth() in App.vue:

<script setup lang="ts">
import { provideLumoAuth } from './composables/useLumoAuth';
provideLumoAuth();
</script>

PKCE sign-in

import { useAuthModule } from './composables/useLumoAuth';

const auth = useAuthModule();

async function signIn() {
const redirectUri = `${window.location.origin}/auth/callback`;
const { url, codeVerifier, state } = await auth.buildAuthorizationUrl({ redirectUri });
sessionStorage.setItem('lumoauth_pkce_verifier', codeVerifier);
sessionStorage.setItem('lumoauth_pkce_state', state);
window.location.href = url;
}

Callback handler

Mount this component at /auth/callback:

<script setup lang="ts">
import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useAuthModule } from '../composables/useLumoAuth';

const router = useRouter();
const auth = useAuthModule();

onMounted(async () => {
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const codeVerifier = sessionStorage.getItem('lumoauth_pkce_verifier');
const savedState = sessionStorage.getItem('lumoauth_pkce_state');

if (!code || returnedState !== savedState || !codeVerifier) {
router.push('/');
return;
}

const tokens = await auth.exchangeCodeForTokens({
code,
codeVerifier,
redirectUri: `${window.location.origin}/auth/callback`,
});

sessionStorage.setItem('lumoauth_access_token', tokens.access_token);
sessionStorage.removeItem('lumoauth_pkce_verifier');
sessionStorage.removeItem('lumoauth_pkce_state');
router.push('/dashboard');
});
</script>

<template><p>Signing in…</p></template>

Permission checks

import { ref, onMounted } from 'vue';
import { useLumoAuth } from '../composables/useLumoAuth';

const client = useLumoAuth();
const canEdit = ref(false);

onMounted(async () => {
canEdit.value = await client.permissions.check('documents.edit');
});