mobilizon/src/components/OAuth/AuthorizeApplication.vue

228 lines
6.1 KiB
Vue
Raw Normal View History

<template>
<div v-if="checkDevice">
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-xl my-6 p-4 pt-1">
<h1 class="text-3xl">
{{ t("Application authorized") }}
</h1>
<p>
{{ t("Check your device to continue. You may now close this window.") }}
</p>
</div>
</div>
<div v-else>
<h1 class="text-3xl">
{{ t("Autorize this application to access your account?") }}
</h1>
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-xl my-6">
<div class="p-4 pb-0">
<p class="text-3xl font-bold">{{ authApplication.name }}</p>
<p>{{ authApplication.website }}</p>
</div>
<p class="p-4">
{{
t(
"You'll be able to revoke access for this application in your account settings."
)
}}
</p>
<div class="">
<div
v-if="collapses.length === 0"
class="rounded-lg bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
>
<AlertCircle :size="42" />
<p>
{{
t(
"This application didn't ask for known permissions. It's likely the request is incorrect."
)
}}
</p>
</div>
<p v-else class="px-4 font-bold">
{{ t("This application asks for the following permissions:") }}
</p>
<o-collapse
class="mt-3 border-b pb-2 border-zinc-700 text-black dark:text-white"
:class="{
'bg-mbz-warning dark:!text-black': collapse?.type === 'warning',
}"
animation="slide"
v-for="(collapse, index) of collapses"
:key="index"
:open="isOpen === index"
@open="isOpen = index"
>
<template #trigger="props">
<div class="flex py-1" role="button">
<o-icon :icon="collapse.icon" class="px-2" />
<p class="font-bold text-lg p-2 flex-1">
{{ collapse.title }}
</p>
<a
class="flex items-center cursor-pointer p-3 justify-center self-end"
>
<o-icon :icon="props.open ? 'chevron-up' : 'chevron-down'">
</o-icon>
</a>
</div>
</template>
<div class="p-2">
<div class="content">
{{ collapse.text }}
</div>
</div>
</o-collapse>
</div>
<div class="flex gap-3 p-4">
<o-button
:disabled="collapses.length === 0"
@click="() => (userCode ? authorizeDevice() : authorize())"
>{{ t("Authorize") }}</o-button
>
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
t("Decline")
}}</o-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { useHead } from "@unhead/vue";
import { computed, inject, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useMutation } from "@vue/apollo-composable";
import {
AUTORIZE_APPLICATION,
AUTORIZE_DEVICE_APPLICATION,
} from "@/graphql/application";
import RouteName from "@/router/name";
import { IApplication } from "@/types/application.model";
import { scope as oAuthScopes } from "./scopes";
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
import { Notifier } from "@/plugins/notifier";
const { t } = useI18n({ useScope: "global" });
const props = defineProps<{
authApplication: IApplication;
redirectURI?: string | null;
state?: string | null;
scope?: string | null;
userCode?: string;
}>();
const isOpen = ref<number>(-1);
const checkDevice = ref(false);
const collapses = computed(() =>
(props.scope ?? "")
.split(" ")
.map((localScope) => oAuthScopes[localScope])
.filter((localScope) => localScope)
);
const {
mutate: authorizeMutation,
onDone: onAuthorizeMutationDone,
onError: onAuthorizeMutationError,
} = useMutation<
{
authorizeApplication: {
code: string;
state: string;
clientId: string;
scope: string;
};
},
{
applicationClientId: string;
redirectURI: string;
state?: string | null;
scope?: string | null;
}
>(AUTORIZE_APPLICATION);
const authorize = () => {
authorizeMutation({
applicationClientId: props.authApplication.clientId,
redirectURI: props.redirectURI as string,
state: props.state,
scope: props.scope,
});
};
const {
mutate: authorizeDeviceMutation,
onDone: onAuthorizeDeviceMutationDone,
} = useMutation<
{
authorizeDeviceApplication: {
clientId: string;
scope: string;
};
},
{
applicationClientId: string;
userCode: string;
}
>(AUTORIZE_DEVICE_APPLICATION);
const authorizeDevice = () => {
authorizeDeviceMutation({
applicationClientId: props.authApplication.clientId,
userCode: props.userCode ?? "",
});
};
onAuthorizeDeviceMutationDone(({ data }) => {
const localClientId = data?.authorizeDeviceApplication?.clientId;
const localScope = data?.authorizeDeviceApplication?.scope;
if (!localClientId || !localScope) return;
checkDevice.value = true;
});
onAuthorizeMutationDone(({ data }) => {
const code = data?.authorizeApplication?.code;
const localClientId = data?.authorizeApplication?.clientId;
const localScope = data?.authorizeApplication?.scope;
const returnedState = data?.authorizeApplication?.state ?? "";
if (!code || !localClientId || !localScope) return;
if (props.redirectURI === "urn:ietf:wg:oauth:2.0:oob") {
checkDevice.value = true;
return;
}
if (props.redirectURI) {
const params = new URLSearchParams(
Object.entries({
code,
state: returnedState,
client_id: localClientId,
scope: localScope,
})
);
window.location.assign(
new URL(`${props.redirectURI}?${params.toString()}`)
);
}
});
const notifier = inject<Notifier>("notifier");
onAuthorizeMutationError(({ graphQLErrors }) => {
graphQLErrors.forEach(({ message }) => {
notifier?.error(message);
});
});
useHead({
title: computed(() => t("Authorize application")),
});
</script>