Skip to content

Commit 82bf6b6

Browse files
Merge pull request #231 from aziontech/ai-auth
feat(templates): adding basic and clerk auth
2 parents 3104603 + fe8b3ab commit 82bf6b6

File tree

11 files changed

+401
-41
lines changed

11 files changed

+401
-41
lines changed

templates/vue/vue3-ai-chatbot-widget/.env.example

+7
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@ VITE_PREVIEW_TEXT=
1818

1919
# VITE_FOOTER_DISCLAIMER: Footer disclaimer text
2020
VITE_FOOTER_DISCLAIMER=
21+
22+
# VITE_AUTH_MODE: Authentication mode
23+
# Options: basic or clerk.
24+
VITE_AUTH_MODE=
25+
26+
# VITE_CLERK_PUBLIC_KEY: Clerk public key
27+
VITE_CLERK_PUBLIC_KEY=

templates/vue/vue3-ai-chatbot-widget/src/App.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
isOpenByDefault: Boolean,
1616
isMaximizedByDefault: Boolean,
1717
previewText: String,
18-
footerDisclaimer: String
18+
footerDisclaimer: String,
19+
authMode: String
1920
})
2021
2122
const chatWidget = reactive({
@@ -27,7 +28,8 @@
2728
isOpenChat: props.isOpenByDefault,
2829
isMaximizedChat: props.isMaximizedByDefault,
2930
previewText: props.previewText,
30-
footerDisclaimer: props.footerDisclaimer
31+
footerDisclaimer: props.footerDisclaimer,
32+
authMode: props.authMode
3133
})
3234
3335
provide('chatWidget', chatWidget)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
.auth-modal-overlay {
2+
position: fixed;
3+
top: 0;
4+
left: 0;
5+
width: 100%;
6+
height: 100%;
7+
background: rgba(0, 0, 0, 0.5);
8+
display: flex;
9+
align-items: center;
10+
justify-content: center;
11+
z-index: 1000;
12+
}
13+
14+
.auth-modal {
15+
background: white;
16+
padding: 2rem;
17+
border-radius: 8px;
18+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
19+
}
20+
21+
.auth-form {
22+
display: flex;
23+
flex-direction: column;
24+
gap: 1rem;
25+
}
26+
27+
input {
28+
padding: 0.5rem;
29+
border: 1px solid #ddd;
30+
border-radius: 4px;
31+
}
32+
33+
button {
34+
padding: 0.5rem 1rem;
35+
background: #f76707; /* Orange color */
36+
color: white;
37+
border: none;
38+
border-radius: 4px;
39+
cursor: pointer;
40+
transition: background-color 0.2s;
41+
}
42+
43+
button:hover {
44+
background: #e85d04; /* Darker orange on hover */
45+
}
46+
47+
button:disabled {
48+
opacity: 0.7;
49+
cursor: not-allowed;
50+
}
51+
52+
.error-message {
53+
color: #dc3545;
54+
font-size: 0.875rem;
55+
padding: 0.5rem;
56+
background: #fde8e8;
57+
border-radius: 4px;
58+
}
59+
60+
.loading-message {
61+
color: #666;
62+
font-size: 0.875rem;
63+
text-align: center;
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<template>
2+
<div class="auth-modal-overlay">
3+
<div class="auth-modal">
4+
<h2>Authentication Required</h2>
5+
<form class="auth-form" @submit.prevent="handleSubmit">
6+
<div v-if="error" class="error-message">
7+
{{ error }}
8+
</div>
9+
<div v-if="loading" class="loading-message">
10+
Loading...
11+
</div>
12+
<input
13+
type="password"
14+
v-model="password"
15+
placeholder="Enter password"
16+
:disabled="loading"
17+
autofocus
18+
/>
19+
<button
20+
type="submit"
21+
:disabled="loading"
22+
>
23+
{{ loading ? 'Signing in...' : 'Sign In' }}
24+
</button>
25+
</form>
26+
</div>
27+
</div>
28+
</template>
29+
30+
<script setup>
31+
import { ref } from 'vue'
32+
33+
const props = defineProps({
34+
onSubmit: {
35+
type: Function,
36+
required: true
37+
}
38+
})
39+
40+
const password = ref('')
41+
const loading = ref(false)
42+
const error = ref('')
43+
44+
const handleSubmit = async () => {
45+
if (!password.value) {
46+
error.value = 'Password is required'
47+
return
48+
}
49+
50+
loading.value = true
51+
error.value = ''
52+
53+
try {
54+
await props.onSubmit(password.value)
55+
} catch (err) {
56+
error.value = err.message || 'Invalid password'
57+
} finally {
58+
loading.value = false
59+
}
60+
}
61+
</script>
62+
63+
<style scoped>
64+
@import '../assets/auth-modal.css';
65+
</style>

templates/vue/vue3-ai-chatbot-widget/src/components/layout-chat/index.vue

+46-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
>
1010
<div
1111
v-if="chatWidget.isOpenChat || chatWidget.isClosing"
12-
class="fixed right-0 z-[55] border surface-ground surface-border transition-transform ease-in-out max-md:w-full max-md:h-full max-md:top-0 max-md:right-0"
13-
:class="chatClass"
12+
:class="[
13+
...chatClass,
14+
{ 'pointer-events-none': showAuthOverlay },
15+
'fixed right-0 z-[55] border surface-ground surface-border transition-transform ease-in-out max-md:w-full max-md:h-full max-md:top-0 max-md:right-0'
16+
]"
1417
@transitionend="onTransitionEnd"
1518
>
1619
<div class="h-full flex flex-col">
@@ -32,22 +35,61 @@
3235
</div>
3336
</div>
3437
</Transition>
38+
39+
<!-- Auth overlay - only show for basic auth -->
40+
<Transition
41+
enter-active-class="transition-all duration-300 ease-out"
42+
enter-from-class="opacity-0"
43+
enter-to-class="opacity-100"
44+
leave-active-class="transition-all duration-300 ease-in"
45+
leave-from-class="opacity-100"
46+
leave-to-class="opacity-0"
47+
>
48+
<div v-if="showAuthOverlay && chatWidget.authMode === 'basic'"
49+
class="fixed inset-0 z-[60] bg-black/50 flex items-center justify-center">
50+
<BasicAuthWindow :onSubmit="handleAuthSubmit" />
51+
</div>
52+
</Transition>
3553
</template>
3654

3755
<script setup>
38-
import { computed, inject } from 'vue'
56+
import { computed, inject, ref } from 'vue'
3957
import ChatHeader from './chat-header.vue'
4058
import ChatBody from './chat-body.vue'
4159
import ChatFooter from './chat-footer.vue'
60+
import BasicAuthWindow from '../BasicAuthWindow.vue'
4261
import { useAzionCopilot } from '../../composables/useAzionCopilot'
62+
import { AuthService } from '../../services/auth'
63+
import { CONSTANTS } from '../../core'
4364
4465
const chatWidget = inject('chatWidget')
66+
const showAuthOverlay = ref(false)
4567
4668
defineOptions({ name: 'layout-chat' })
4769
48-
const { messages, sendMessage, cancelMessage, resetChat, isProcessingRequest, sendFeedback } =
70+
const { messages, sendMessage, cancelMessage, resetChat, isProcessingRequest, sendFeedback, copilot } =
4971
useAzionCopilot({ server: chatWidget.serverUrl })
5072
73+
copilot.on(CONSTANTS.EVENTS.AUTH_REQUIRED, () => {
74+
console.log(chatWidget)
75+
if (chatWidget.authMode === 'basic') {
76+
showAuthOverlay.value = true
77+
}
78+
})
79+
80+
const handleAuthSubmit = async (password) => {
81+
const authService = new AuthService({
82+
authMode: 'basic',
83+
copilotBackend: chatWidget.serverUrl.url
84+
})
85+
86+
const result = await authService.fetchBasicAuth(password)
87+
if (result.token) {
88+
copilot.setAuthToken(result.token)
89+
showAuthOverlay.value = false
90+
}
91+
}
92+
5193
const closeChat = () => {
5294
chatWidget.isClosing = true
5395
}

templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
:class="classRoleApply"
55
>
66
<div
7-
v-if="isSystem"
7+
v-if="isAssistant"
88
class="flex gap-3 mt-1"
99
>
1010
<Avatar />
1111
</div>
1212
<div class="message-content">
13-
<div v-if="!isSystem">
13+
<div v-if="!isAssistant">
1414
<div
1515
v-html="formattedMessage"
1616
class="formatted-content"
@@ -149,14 +149,14 @@
149149
}
150150
})
151151
152-
const isSystem = computed(() => props.message.role === 'system')
152+
const isAssistant = computed(() => props.message.role === 'assistant')
153153
const messageReadingStatus = computed(
154154
() => props.message.status === CONSTANTS.STATUS.MESSAGES.RESPONDING
155155
)
156156
157157
const classRole = {
158158
user: 'surface-300 ml-auto break-words w-fit rounded-lg h-fit px-4 py-3',
159-
system: 'mr-auto w-full mt-3'
159+
assistant: 'mr-auto w-full mt-3'
160160
}
161161
162162
const classRoleApply = classRole[props.message.role]

templates/vue/vue3-ai-chatbot-widget/src/composables/useAzionCopilot.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { AzionCopilot, CONSTANTS } from '../core'
33

44
export function useAzionCopilot(config) {
55
const copilot = new AzionCopilot(config)
6+
7+
const token = sessionStorage.getItem('copilot_auth_token')
8+
if (token) {
9+
copilot.setAuthToken(token)
10+
}
611

712
const messages = ref([])
813
const isProcessingRequest = ref(false)
@@ -40,6 +45,7 @@ export function useAzionCopilot(config) {
4045
sendFeedback,
4146
resetChat,
4247
isProcessingRequest,
43-
cancelMessage
48+
cancelMessage,
49+
copilot
4450
}
4551
}

0 commit comments

Comments
 (0)