een-api-toolkit 0.3.20 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/docs-accuracy-reviewer.md +146 -0
- package/.claude/agents/een-auth-agent.md +168 -0
- package/.claude/agents/een-devices-agent.md +294 -0
- package/.claude/agents/een-events-agent.md +375 -0
- package/.claude/agents/een-media-agent.md +256 -0
- package/.claude/agents/een-setup-agent.md +126 -0
- package/.claude/agents/een-users-agent.md +239 -0
- package/.claude/agents/test-runner.md +144 -0
- package/CHANGELOG.md +138 -30
- package/docs/AI-CONTEXT.md +169 -1700
- package/docs/ai-reference/AI-AUTH.md +288 -0
- package/docs/ai-reference/AI-DEVICES.md +569 -0
- package/docs/ai-reference/AI-EVENTS.md +1745 -0
- package/docs/ai-reference/AI-MEDIA.md +974 -0
- package/docs/ai-reference/AI-SETUP.md +267 -0
- package/docs/ai-reference/AI-USERS.md +255 -0
- package/examples/vue-event-subscriptions/package-lock.json +8 -1
- package/examples/vue-event-subscriptions/package.json +1 -0
- package/examples/vue-event-subscriptions/src/App.vue +1 -41
- package/examples/vue-event-subscriptions/src/composables/useHlsPlayer.ts +272 -0
- package/examples/vue-event-subscriptions/src/main.ts +3 -3
- package/examples/vue-event-subscriptions/src/stores/connection.ts +101 -0
- package/examples/vue-event-subscriptions/src/stores/mediaSession.ts +79 -0
- package/examples/vue-event-subscriptions/src/views/LiveEvents.vue +349 -88
- package/examples/vue-event-subscriptions/src/views/Logout.vue +6 -0
- package/examples/vue-event-subscriptions/src/views/Subscriptions.vue +0 -13
- package/examples/vue-events/package-lock.json +8 -1
- package/examples/vue-events/package.json +1 -0
- package/examples/vue-events/src/components/EventsModal.vue +269 -47
- package/examples/vue-events/src/composables/useHlsPlayer.ts +272 -0
- package/examples/vue-events/src/stores/mediaSession.ts +79 -0
- package/package.json +10 -2
- package/scripts/setup-agents.ts +116 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# Authentication - EEN API Toolkit
|
|
2
|
+
|
|
3
|
+
> **Version:** 0.3.22
|
|
4
|
+
>
|
|
5
|
+
> OAuth flow implementation, token management, and session handling.
|
|
6
|
+
> Load this document when implementing login, logout, or auth guards.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The toolkit uses OAuth 2.0 with a proxy server for secure token management:
|
|
13
|
+
|
|
14
|
+
1. **User clicks login** → Redirect to EEN Identity Provider
|
|
15
|
+
2. **User authenticates** → EEN redirects back with auth code
|
|
16
|
+
3. **App exchanges code** → Proxy exchanges code for tokens
|
|
17
|
+
4. **Refresh token stored** → Proxy keeps refresh token secure (never sent to client)
|
|
18
|
+
5. **Access token returned** → Client stores short-lived access token
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Authentication Functions
|
|
23
|
+
|
|
24
|
+
### getAuthUrl()
|
|
25
|
+
|
|
26
|
+
Generate the OAuth authorization URL. Redirect the user here to start login.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { getAuthUrl } from 'een-api-toolkit'
|
|
30
|
+
|
|
31
|
+
function login() {
|
|
32
|
+
window.location.href = getAuthUrl()
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### handleAuthCallback(code, state)
|
|
37
|
+
|
|
38
|
+
Handle the OAuth callback. Call this when user returns to your redirect URI.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { handleAuthCallback } from 'een-api-toolkit'
|
|
42
|
+
|
|
43
|
+
const { data, error } = await handleAuthCallback(code, state)
|
|
44
|
+
|
|
45
|
+
if (error) {
|
|
46
|
+
console.error('Auth failed:', error.message)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// User is now authenticated
|
|
51
|
+
router.push('/dashboard')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### refreshToken()
|
|
55
|
+
|
|
56
|
+
Manually refresh the access token. Usually handled automatically.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { refreshToken } from 'een-api-toolkit'
|
|
60
|
+
|
|
61
|
+
const { data, error } = await refreshToken()
|
|
62
|
+
if (error) {
|
|
63
|
+
// Refresh failed - redirect to login
|
|
64
|
+
router.push('/login')
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### revokeToken()
|
|
69
|
+
|
|
70
|
+
Revoke the token and logout.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { revokeToken } from 'een-api-toolkit'
|
|
74
|
+
|
|
75
|
+
async function logout() {
|
|
76
|
+
await revokeToken()
|
|
77
|
+
router.push('/login')
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Auth Store (useAuthStore)
|
|
84
|
+
|
|
85
|
+
Access authentication state directly:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { useAuthStore } from 'een-api-toolkit'
|
|
89
|
+
|
|
90
|
+
const authStore = useAuthStore()
|
|
91
|
+
|
|
92
|
+
// Reactive state
|
|
93
|
+
authStore.isAuthenticated // boolean
|
|
94
|
+
authStore.token // Access token (or null)
|
|
95
|
+
authStore.baseUrl // EEN API base URL (e.g., https://c001.eagleeyenetworks.com)
|
|
96
|
+
authStore.sessionId // Session identifier
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Vue Router Auth Guard
|
|
102
|
+
|
|
103
|
+
Protect routes that require authentication:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { useAuthStore } from 'een-api-toolkit'
|
|
107
|
+
|
|
108
|
+
router.beforeEach((to, _from, next) => {
|
|
109
|
+
const authStore = useAuthStore()
|
|
110
|
+
|
|
111
|
+
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
|
|
112
|
+
next({ name: 'login' })
|
|
113
|
+
} else {
|
|
114
|
+
next()
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Component Templates
|
|
122
|
+
|
|
123
|
+
### Login.vue
|
|
124
|
+
|
|
125
|
+
```vue
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { getAuthUrl } from 'een-api-toolkit'
|
|
128
|
+
|
|
129
|
+
function login() {
|
|
130
|
+
// Redirect to EEN OAuth login
|
|
131
|
+
window.location.href = getAuthUrl()
|
|
132
|
+
}
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<template>
|
|
136
|
+
<div class="login">
|
|
137
|
+
<h2 data-testid="login-title">Login</h2>
|
|
138
|
+
<p>Click the button below to login with your Eagle Eye Networks account.</p>
|
|
139
|
+
<button data-testid="login-button" @click="login">Login with Eagle Eye Networks</button>
|
|
140
|
+
</div>
|
|
141
|
+
</template>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Callback.vue
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<script setup lang="ts">
|
|
148
|
+
import { onMounted, ref } from 'vue'
|
|
149
|
+
import { useRouter } from 'vue-router'
|
|
150
|
+
import { handleAuthCallback } from 'een-api-toolkit'
|
|
151
|
+
|
|
152
|
+
const router = useRouter()
|
|
153
|
+
const error = ref<string | null>(null)
|
|
154
|
+
const processing = ref(true)
|
|
155
|
+
|
|
156
|
+
onMounted(async () => {
|
|
157
|
+
const url = new URL(window.location.href)
|
|
158
|
+
const code = url.searchParams.get('code')
|
|
159
|
+
const state = url.searchParams.get('state')
|
|
160
|
+
const errorParam = url.searchParams.get('error')
|
|
161
|
+
|
|
162
|
+
if (errorParam) {
|
|
163
|
+
error.value = `OAuth error: ${errorParam}`
|
|
164
|
+
processing.value = false
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!code || !state) {
|
|
169
|
+
error.value = 'Missing authorization code or state parameter'
|
|
170
|
+
processing.value = false
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const result = await handleAuthCallback(code, state)
|
|
175
|
+
|
|
176
|
+
if (result.error) {
|
|
177
|
+
error.value = result.error.message
|
|
178
|
+
processing.value = false
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Success - redirect to home
|
|
183
|
+
router.push('/')
|
|
184
|
+
})
|
|
185
|
+
</script>
|
|
186
|
+
|
|
187
|
+
<template>
|
|
188
|
+
<div class="callback">
|
|
189
|
+
<div v-if="processing" class="loading">
|
|
190
|
+
<h2>Authenticating...</h2>
|
|
191
|
+
<p>Please wait while we complete the login process.</p>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div v-else-if="error" class="error-state">
|
|
195
|
+
<h2>Authentication Failed</h2>
|
|
196
|
+
<p class="error">{{ error }}</p>
|
|
197
|
+
<router-link to="/login">
|
|
198
|
+
<button>Try Again</button>
|
|
199
|
+
</router-link>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</template>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Logout.vue
|
|
206
|
+
|
|
207
|
+
```vue
|
|
208
|
+
<script setup lang="ts">
|
|
209
|
+
import { onMounted, ref } from 'vue'
|
|
210
|
+
import { useRouter } from 'vue-router'
|
|
211
|
+
import { revokeToken } from 'een-api-toolkit'
|
|
212
|
+
|
|
213
|
+
const router = useRouter()
|
|
214
|
+
const processing = ref(true)
|
|
215
|
+
const error = ref<string | null>(null)
|
|
216
|
+
|
|
217
|
+
onMounted(async () => {
|
|
218
|
+
const result = await revokeToken()
|
|
219
|
+
|
|
220
|
+
if (result.error) {
|
|
221
|
+
// Even if revoke fails, the local state is cleared
|
|
222
|
+
console.warn('Token revocation failed:', result.error.message)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
processing.value = false
|
|
226
|
+
|
|
227
|
+
// Redirect to home after a short delay
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
router.push('/')
|
|
230
|
+
}, 2000)
|
|
231
|
+
})
|
|
232
|
+
</script>
|
|
233
|
+
|
|
234
|
+
<template>
|
|
235
|
+
<div class="logout">
|
|
236
|
+
<div v-if="processing">
|
|
237
|
+
<h2>Logging out...</h2>
|
|
238
|
+
<p class="loading">Please wait.</p>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div v-else>
|
|
242
|
+
<h2>Logged Out</h2>
|
|
243
|
+
<p>You have been successfully logged out.</p>
|
|
244
|
+
<p v-if="error" class="error">Note: {{ error }}</p>
|
|
245
|
+
<p class="redirect">Redirecting to home page...</p>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</template>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Token Lifecycle
|
|
254
|
+
|
|
255
|
+
| Event | Action |
|
|
256
|
+
|-------|--------|
|
|
257
|
+
| App loads | Check for existing token in storage |
|
|
258
|
+
| Token expires | Auto-refresh triggered (5 min before expiry) |
|
|
259
|
+
| Refresh fails | Clear auth state, redirect to login |
|
|
260
|
+
| User logs out | Revoke token, clear storage |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Storage Strategies
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { getStorageStrategy, STORAGE_STRATEGY_DESCRIPTIONS } from 'een-api-toolkit'
|
|
268
|
+
|
|
269
|
+
const strategy = getStorageStrategy()
|
|
270
|
+
const description = STORAGE_STRATEGY_DESCRIPTIONS[strategy]
|
|
271
|
+
// e.g., "localStorage: persists across sessions"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Common Auth Errors
|
|
277
|
+
|
|
278
|
+
| Error Code | Cause | Solution |
|
|
279
|
+
|------------|-------|----------|
|
|
280
|
+
| `AUTH_REQUIRED` | No valid token | Redirect to login |
|
|
281
|
+
| `AUTH_FAILED` | Invalid credentials | Show error, allow retry |
|
|
282
|
+
| `TOKEN_EXPIRED` | Token expired | Auto-refresh or re-login |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Reference Example
|
|
287
|
+
|
|
288
|
+
See `examples/vue-users/src/views/Login.vue`, `Callback.vue`, `Logout.vue`
|