fix(apps): fix device flow authorization process
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
c9d20748a4
commit
9a457fb011
@ -1,5 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<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">
|
<h1 class="text-3xl">
|
||||||
{{ t("Autorize this application to access your account?") }}
|
{{ t("Autorize this application to access your account?") }}
|
||||||
</h1>
|
</h1>
|
||||||
@ -68,7 +78,7 @@
|
|||||||
<div class="flex gap-3 p-4">
|
<div class="flex gap-3 p-4">
|
||||||
<o-button
|
<o-button
|
||||||
:disabled="collapses.length === 0"
|
:disabled="collapses.length === 0"
|
||||||
@click="() => authorize()"
|
@click="() => (userCode ? authorizeDevice() : authorize())"
|
||||||
>{{ t("Authorize") }}</o-button
|
>{{ t("Authorize") }}</o-button
|
||||||
>
|
>
|
||||||
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
|
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
|
||||||
@ -84,7 +94,10 @@ import { useHead } from "@vueuse/head";
|
|||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useMutation } from "@vue/apollo-composable";
|
import { useMutation } from "@vue/apollo-composable";
|
||||||
import { AUTORIZE_APPLICATION } from "@/graphql/application";
|
import {
|
||||||
|
AUTORIZE_APPLICATION,
|
||||||
|
AUTORIZE_DEVICE_APPLICATION,
|
||||||
|
} from "@/graphql/application";
|
||||||
import RouteName from "@/router/name";
|
import RouteName from "@/router/name";
|
||||||
import { IApplication } from "@/types/application.model";
|
import { IApplication } from "@/types/application.model";
|
||||||
import { scope as oAuthScopes } from "./scopes";
|
import { scope as oAuthScopes } from "./scopes";
|
||||||
@ -97,9 +110,11 @@ const props = defineProps<{
|
|||||||
redirectURI?: string | null;
|
redirectURI?: string | null;
|
||||||
state?: string | null;
|
state?: string | null;
|
||||||
scope?: string | null;
|
scope?: string | null;
|
||||||
|
userCode?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isOpen = ref<number>(-1);
|
const isOpen = ref<number>(-1);
|
||||||
|
const checkDevice = ref(false);
|
||||||
|
|
||||||
const collapses = computed(() =>
|
const collapses = computed(() =>
|
||||||
(props.scope ?? "")
|
(props.scope ?? "")
|
||||||
@ -135,6 +150,37 @@ const authorize = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 }) => {
|
onAuthorizeMutationDone(({ data }) => {
|
||||||
const code = data?.authorizeApplication?.code;
|
const code = data?.authorizeApplication?.code;
|
||||||
const localClientId = data?.authorizeApplication?.clientId;
|
const localClientId = data?.authorizeApplication?.clientId;
|
||||||
@ -143,6 +189,11 @@ onAuthorizeMutationDone(({ data }) => {
|
|||||||
|
|
||||||
if (!code || !localClientId || !localScope) return;
|
if (!code || !localClientId || !localScope) return;
|
||||||
|
|
||||||
|
if (props.redirectURI === "urn:ietf:wg:oauth:2.0:oob") {
|
||||||
|
checkDevice.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (props.redirectURI) {
|
if (props.redirectURI) {
|
||||||
const params = new URLSearchParams(
|
const params = new URLSearchParams(
|
||||||
Object.entries({
|
Object.entries({
|
||||||
|
@ -35,7 +35,7 @@ export const AUTORIZE_APPLICATION = gql`
|
|||||||
export const AUTORIZE_DEVICE_APPLICATION = gql`
|
export const AUTORIZE_DEVICE_APPLICATION = gql`
|
||||||
mutation AuthorizeDeviceApplication(
|
mutation AuthorizeDeviceApplication(
|
||||||
$applicationClientId: String!
|
$applicationClientId: String!
|
||||||
$userCode: String
|
$userCode: String!
|
||||||
) {
|
) {
|
||||||
authorizeDeviceApplication(
|
authorizeDeviceApplication(
|
||||||
clientId: $applicationClientId
|
clientId: $applicationClientId
|
||||||
|
@ -57,7 +57,12 @@
|
|||||||
<o-button native-type="submit">{{ t("Continue") }}</o-button>
|
<o-button native-type="submit">{{ t("Continue") }}</o-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<AuthorizeApplication v-if="application" :auth-application="application" />
|
<AuthorizeApplication
|
||||||
|
v-if="application"
|
||||||
|
:auth-application="application"
|
||||||
|
:user-code="code"
|
||||||
|
:scope="scope"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -85,14 +90,14 @@ const {
|
|||||||
const inputs = reactive<string[]>([]);
|
const inputs = reactive<string[]>([]);
|
||||||
|
|
||||||
const application = ref<IApplication | null>(null);
|
const application = ref<IApplication | null>(null);
|
||||||
|
const scope = ref<string | null>(null);
|
||||||
|
|
||||||
onDeviceActivationDone(({ data }) => {
|
onDeviceActivationDone(({ data }) => {
|
||||||
|
console.debug("onDeviceActivationDone", data);
|
||||||
const foundApplication = data?.deviceActivation?.application;
|
const foundApplication = data?.deviceActivation?.application;
|
||||||
if (foundApplication) {
|
if (foundApplication) {
|
||||||
application.value = {
|
application.value = foundApplication;
|
||||||
...foundApplication,
|
scope.value = data?.deviceActivation?.scope;
|
||||||
scope: data?.deviceActivation?.scope,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -117,7 +122,11 @@ const error = ref<string | null>(null);
|
|||||||
|
|
||||||
onDeviceActivationError(
|
onDeviceActivationError(
|
||||||
({ graphQLErrors }: { graphQLErrors: AbsintheGraphQLErrors }) => {
|
({ graphQLErrors }: { graphQLErrors: AbsintheGraphQLErrors }) => {
|
||||||
if (graphQLErrors[0].status_code === 404) {
|
const err = graphQLErrors[0];
|
||||||
|
if (
|
||||||
|
err.status_code === 400 &&
|
||||||
|
err.code === "device_application_code_expired"
|
||||||
|
) {
|
||||||
error.value = t("The device code is incorrect or no longer valid.");
|
error.value = t("The device code is incorrect or no longer valid.");
|
||||||
}
|
}
|
||||||
resetInputs();
|
resetInputs();
|
||||||
|
@ -5,6 +5,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
|||||||
|
|
||||||
alias Mobilizon.Applications, as: ApplicationManager
|
alias Mobilizon.Applications, as: ApplicationManager
|
||||||
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation, ApplicationToken}
|
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation, ApplicationToken}
|
||||||
|
alias Mobilizon.GraphQL.Error
|
||||||
alias Mobilizon.Service.Auth.Applications
|
alias Mobilizon.Service.Auth.Applications
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
import Mobilizon.Web.Gettext, only: [dgettext: 2]
|
import Mobilizon.Web.Gettext, only: [dgettext: 2]
|
||||||
@ -17,7 +18,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
|||||||
@spec authorize(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} | {:error, String.t()}
|
@spec authorize(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} | {:error, String.t()}
|
||||||
def authorize(
|
def authorize(
|
||||||
_parent,
|
_parent,
|
||||||
%{client_id: client_id, redirect_uri: redirect_uri, scope: scope, state: state},
|
%{client_id: client_id, redirect_uri: redirect_uri, scope: scope} = args,
|
||||||
%{context: %{current_user: %User{id: user_id}}}
|
%{context: %{current_user: %User{id: user_id}}}
|
||||||
) do
|
) do
|
||||||
case Applications.autorize(client_id, redirect_uri, scope, user_id) do
|
case Applications.autorize(client_id, redirect_uri, scope, user_id) do
|
||||||
@ -27,7 +28,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
|||||||
scope: scope,
|
scope: scope,
|
||||||
authorization_code: code
|
authorization_code: code
|
||||||
}} ->
|
}} ->
|
||||||
{:ok, %{code: code, state: state, client_id: client_id, scope: scope}}
|
{:ok, %{code: code, state: Map.get(args, :state), client_id: client_id, scope: scope}}
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = err} ->
|
{:error, %Ecto.Changeset{} = err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
@ -106,7 +107,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
|||||||
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
|
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
|
||||||
|
|
||||||
{:error, :expired} ->
|
{:error, :expired} ->
|
||||||
{:error, dgettext("errors", "The given user code has expired")}
|
{:error,
|
||||||
|
%Error{
|
||||||
|
message: dgettext("errors", "The given user code has expired"),
|
||||||
|
status_code: 400,
|
||||||
|
code: :device_application_code_expired
|
||||||
|
}}
|
||||||
|
|
||||||
{:error, :not_found} ->
|
{:error, :not_found} ->
|
||||||
{:error, dgettext("errors", "The given user code is invalid")}
|
{:error, dgettext("errors", "The given user code is invalid")}
|
||||||
@ -143,7 +149,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{:error, :expired} ->
|
{:error, :expired} ->
|
||||||
{:error, dgettext("errors", "The given user code has expired")}
|
{:error,
|
||||||
|
%Error{
|
||||||
|
message: dgettext("errors", "The given user code has expired"),
|
||||||
|
status_code: 400,
|
||||||
|
code: :device_application_code_expired
|
||||||
|
}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
|||||||
resolve(&Application.activate_device/3)
|
resolve(&Application.activate_device/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Activate an user device"
|
@desc "Authorize an user device"
|
||||||
field :authorize_device_application, :auth_application do
|
field :authorize_device_application, :auth_application do
|
||||||
arg(:client_id, non_null(:string), description: "The application's client_id")
|
arg(:client_id, non_null(:string), description: "The application's client_id")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user