♻️ OAuth authenticate

This commit is contained in:
2024-06-26 14:47:34 +08:00
parent 0d02eca76e
commit 21d3d71936
15 changed files with 328 additions and 240 deletions

View File

@@ -31,7 +31,7 @@
<p class="opacity-80 text-xs">Permissions they requested</p>
<v-card variant="tonal" class="mt-1 mx-[-4px]">
<v-list density="compact">
<v-list-item v-for="claim in requestedClaims" lines="two">
<v-list-item v-for="(claim, key) in requestedClaims" :key="key" lines="two">
<template #title>
<span class="capitalize">{{ getClaimDescription(claim)?.name }}</span>
</template>
@@ -106,8 +106,8 @@ const requestedClaims = computed(() => {
const panel = ref("confirm")
async function preconnect() {
const res = await request(`/api/auth/o/connect${location.search}`, {
async function tryAuthorize() {
const res = await request(`/api/auth/o/authorize${location.search}`, {
headers: { Authorization: `Bearer ${getAtk()}` },
})
@@ -116,9 +116,9 @@ async function preconnect() {
} else {
const data = await res.json()
if (data["session"]) {
if (data["ticket"]) {
panel.value = "callback"
callback(data["session"])
callback(data["ticket"])
} else {
document.title = `Solarpass | Connect to ${data["client"]?.name}`
metadata.value = data["client"]
@@ -127,7 +127,7 @@ async function preconnect() {
}
}
preconnect()
tryAuthorize()
function decline() {
if (window.history.length > 0) {
@@ -140,7 +140,7 @@ function decline() {
async function approve() {
loading.value = true
const res = await request(
"/api/auth/o/connect?" +
"/api/auth/o/authorize?" +
new URLSearchParams({
client_id: route.query["client_id"] as string,
redirect_uri: encodeURIComponent(route.query["redirect_uri"] as string),
@@ -159,17 +159,21 @@ async function approve() {
} else {
const data = await res.json()
panel.value = "callback"
setTimeout(() => callback(data["session"]), 1850)
setTimeout(() => callback(data["ticket"]), 1850)
}
}
function callback(session: any) {
const url = `${route.query["redirect_uri"]}?code=${session["grant_token"]}&state=${route.query["state"]}`
function callback(ticket: any) {
const url = `${route.query["redirect_uri"]}?code=${ticket["grant_token"]}&state=${route.query["state"]}`
window.open(url, "_self")
}
function getClaimDescription(key: string): ClaimType {
return claims.hasOwnProperty(key) ? claims[key] : { icon: "mdi-asterisk", name: key, description: "Unknown claim..." }
return Object.prototype.hasOwnProperty.call(claims, key) ? claims[key] : {
icon: "mdi-asterisk",
name: key,
description: "Unknown claim...",
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<div>
<v-expansion-panels>
<v-expansion-panel eager title="Challenges">
<v-expansion-panel eager title="Tickets">
<template #text>
<v-card :loading="reverting.challenges" variant="outlined">
<v-card :loading="reverting.tickets" variant="outlined">
<v-data-table-server
density="compact"
:headers="dataDefinitions.challenges"
:items="challenges"
:items-length="pagination.challenges.total"
:loading="reverting.challenges"
v-model:items-per-page="pagination.challenges.pageSize"
@update:options="readChallenges"
:headers="dataDefinitions.tickets"
:items="tickets"
:items-length="pagination.tickets.total"
:loading="reverting.tickets"
v-model:items-per-page="pagination.tickets.pageSize"
@update:options="readTickets"
item-value="id"
>
<template v-slot:item="{ item }: { item: any }">
@@ -28,40 +28,6 @@
</v-tooltip>
</td>
<td>{{ new Date(item.created_at).toLocaleString() }}</td>
</tr>
</template>
</v-data-table-server>
</v-card>
</template>
</v-expansion-panel>
<v-expansion-panel eager title="Sessions">
<template #text>
<v-card :loading="reverting.sessions" variant="outlined">
<v-data-table-server
density="compact"
:headers="dataDefinitions.sessions"
:items="sessions"
:items-length="pagination.sessions.total"
:loading="reverting.sessions"
v-model:items-per-page="pagination.sessions.pageSize"
@update:options="readSessions"
item-value="id"
>
<template v-slot:item="{ item }: { item: any }">
<tr>
<td>{{ item.id }}</td>
<td>
<v-chip v-for="value in item.audiences" size="x-small" color="warning" class="capitalize">
{{ value }}
</v-chip>
</td>
<td>
<v-chip v-for="value in item.claims" size="x-small" color="info" class="font-mono">
{{ value }}
</v-chip>
</td>
<td>{{ new Date(item.created_at).toLocaleString() }}</td>
<td>
<v-tooltip text="Sign out">
<template #activator="{ props }">
@@ -71,7 +37,7 @@
size="x-small"
color="error"
icon="mdi-logout-variant"
@click="killSession(item)"
@click="killTicket(item)"
/>
</template>
</v-tooltip>
@@ -124,25 +90,17 @@
<script setup lang="ts">
import { request } from "@/scripts/request"
import { getAtk, useUserinfo } from "@/stores/userinfo"
import { getAtk } from "@/stores/userinfo"
import { reactive, ref } from "vue"
const id = useUserinfo()
const error = ref<string | null>(null)
const dataDefinitions: { [id: string]: any[] } = {
challenges: [
tickets: [
{ align: "start", key: "id", title: "ID" },
{ align: "start", key: "ip_address", title: "IP Address" },
{ align: "start", key: "user_agent", title: "User Agent" },
{ align: "start", key: "created_at", title: "Issued At" },
],
sessions: [
{ align: "start", key: "id", title: "ID" },
{ align: "start", key: "audiences", title: "Audiences" },
{ align: "start", key: "claims", title: "Claims" },
{ align: "start", key: "created_at", title: "Issued At" },
{ align: "start", key: "actions", title: "Actions", sortable: false },
],
events: [
@@ -155,52 +113,25 @@ const dataDefinitions: { [id: string]: any[] } = {
],
}
const challenges = ref<any>([])
const sessions = ref<any>([])
const tickets = ref<any>([])
const events = ref<any>([])
const reverting = reactive({ challenges: false, sessions: false, events: false })
const reverting = reactive({ tickets: false, sessions: false, events: false })
const pagination = reactive({
challenges: { page: 1, pageSize: 5, total: 0 },
sessions: { page: 1, pageSize: 5, total: 0 },
tickets: { page: 1, pageSize: 5, total: 0 },
events: { page: 1, pageSize: 5, total: 0 },
})
async function readChallenges({ page, itemsPerPage }: { page?: number; itemsPerPage?: number }) {
if (itemsPerPage) pagination.challenges.pageSize = itemsPerPage
if (page) pagination.challenges.page = page
reverting.challenges = true
const res = await request(
"/api/users/me/challenges?" +
new URLSearchParams({
take: pagination.challenges.pageSize.toString(),
offset: ((pagination.challenges.page - 1) * pagination.challenges.pageSize).toString(),
}),
{
headers: { Authorization: `Bearer ${getAtk()}` },
},
)
if (res.status !== 200) {
error.value = await res.text()
} else {
const data = await res.json()
challenges.value = data["data"]
pagination.challenges.total = data["count"]
}
reverting.challenges = false
}
async function readSessions({ page, itemsPerPage }: { page?: number; itemsPerPage?: number }) {
if (itemsPerPage) pagination.sessions.pageSize = itemsPerPage
if (page) pagination.sessions.page = page
async function readTickets({ page, itemsPerPage }: { page?: number; itemsPerPage?: number }) {
if (itemsPerPage) pagination.tickets.pageSize = itemsPerPage
if (page) pagination.tickets.page = page
reverting.sessions = true
const res = await request(
"/api/users/me/sessions?" +
"/api/users/me/tickets?" +
new URLSearchParams({
take: pagination.sessions.pageSize.toString(),
offset: ((pagination.sessions.page - 1) * pagination.sessions.pageSize).toString(),
take: pagination.tickets.pageSize.toString(),
offset: ((pagination.tickets.page - 1) * pagination.tickets.pageSize).toString(),
}),
{
headers: { Authorization: `Bearer ${getAtk()}` },
@@ -210,8 +141,8 @@ async function readSessions({ page, itemsPerPage }: { page?: number; itemsPerPag
error.value = await res.text()
} else {
const data = await res.json()
sessions.value = data["data"]
pagination.sessions.total = data["count"]
tickets.value = data["data"]
pagination.tickets.total = data["count"]
}
reverting.sessions = false
}
@@ -241,18 +172,18 @@ async function readEvents({ page, itemsPerPage }: { page?: number; itemsPerPage?
reverting.events = false
}
Promise.all([readChallenges({}), readSessions({}), readEvents({})])
Promise.all([readTickets({}), readEvents({})])
async function killSession(item: any) {
async function killTicket(item: any) {
reverting.sessions = true
const res = await request(`/api/users/me/sessions/${item.id}`, {
const res = await request(`/api/users/me/tickets/${item.id}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${getAtk()}` },
})
if (res.status !== 200) {
error.value = await res.text()
} else {
await readSessions({})
await readTickets({})
error.value = null
}
reverting.sessions = false