Merge branch 'fixes' into 'main'

fix(auth): small front fixes in 3rd-party auth provider callback

See merge request framasoft/mobilizon!1451
This commit is contained in:
Thomas Citharel 2023-09-07 11:12:20 +00:00
commit 384864cc82
9 changed files with 97 additions and 69 deletions

View File

@ -123,7 +123,7 @@ config :mobilizon, Mobilizon.Web.Email.Mailer,
# can be `true` # can be `true`
# ssl: false, # ssl: false,
# can be `:always` or `:never` # can be `:always` or `:never`
tls: :if_available, tls: :never,
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
tls_options: [ tls_options: [
verify: :verify_peer, verify: :verify_peer,

View File

@ -65,9 +65,15 @@ body {
.field-label { .field-label {
@apply block text-gray-700 dark:text-gray-100 text-base font-bold mb-2; @apply block text-gray-700 dark:text-gray-100 text-base font-bold mb-2;
} }
.o-field--horizontal.field {
@apply items-center;
}
.o-field__horizontal-label .field-label { .o-field__horizontal-label .field-label {
@apply mb-0; @apply mb-0;
} }
.o-field__horizontal-body > .field {
@apply mt-0;
}
.field-danger { .field-danger {
@apply text-red-500; @apply text-red-500;
} }

View File

@ -80,7 +80,7 @@ export function registerAccount() {
) => { ) => {
if (context?.userAlreadyActivated) { if (context?.userAlreadyActivated) {
const currentUserData = store.readQuery<{ const currentUserData = store.readQuery<{
loggedUser: Pick<ICurrentUser, "actors">; loggedUser: Pick<ICurrentUser, "actors" | "id">;
}>({ }>({
query: IDENTITIES, query: IDENTITIES,
}); });

View File

@ -10,3 +10,11 @@ export function htmlToText(html: string) {
template.remove(); template.remove();
return text; return text;
} }
export const getValueFromMeta = (name: string): string | null => {
const element = document.querySelector(`meta[name="${name}"]`);
if (element && element.getAttribute("content")) {
return element.getAttribute("content");
}
return null;
};

View File

@ -24,7 +24,7 @@
required required
v-model="identity.name" v-model="identity.name"
id="identityName" id="identityName"
@input="(event) => updateUsername(event.target.value)" @input="(event: any) => updateUsername(event.target.value)"
/> />
</o-field> </o-field>
@ -138,6 +138,7 @@ import { registerAccount } from "@/composition/apollo/user";
import { convertToUsername } from "@/utils/username"; import { convertToUsername } from "@/utils/username";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useHead } from "@vueuse/head"; import { useHead } from "@vueuse/head";
import { getValueFromMeta } from "@/utils/html";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -174,6 +175,14 @@ onBeforeMount(() => {
if (!props.email) { if (!props.email) {
router.replace({ name: RouteName.PAGE_NOT_FOUND }); router.replace({ name: RouteName.PAGE_NOT_FOUND });
} }
const username = getValueFromMeta("auth-user-suggested-actor-username");
const name = getValueFromMeta("auth-user-suggested-actor-name");
if (username) {
identity.value.preferredUsername = username;
}
if (name) {
identity.value.name = name;
}
}); });
const updateUsername = (value: string) => { const updateUsername = (value: string) => {

View File

@ -2,24 +2,23 @@
<div> <div>
<breadcrumbs-nav :links="breadcrumbsLinks" /> <breadcrumbs-nav :links="breadcrumbsLinks" />
<div v-if="identity"> <div v-if="identity">
<h1> <h1 class="flex justify-center">
<span v-if="isUpdate" class="line-clamp-2">{{ <span v-if="isUpdate" class="line-clamp-2">{{
displayName(identity) displayName(identity)
}}</span> }}</span>
<span v-else>{{ $t("I create an identity") }}</span> <span v-else>{{ t("I create an identity") }}</span>
</h1> </h1>
<o-field horizontal :label="t('Avatar')"> <o-field horizontal :label="t('Avatar')">
<picture-upload <picture-upload
v-model="avatarFile" v-model="avatarFile"
:defaultImage="identity.avatar" :defaultImage="identity.avatar"
:maxSize="avatarMaxSize" :maxSize="avatarMaxSize"
class="picture-upload"
/> />
</o-field> </o-field>
<o-field <o-field
horizontal horizontal
:label="$t('Display name')" :label="t('Display name')"
label-for="identity-display-name" label-for="identity-display-name"
> >
<o-input <o-input
@ -34,14 +33,15 @@
<o-field <o-field
horizontal horizontal
custom-class="username-field" class="username-field"
expanded :label="t('Username')"
:label="$t('Username')"
label-for="identity-username" label-for="identity-username"
:message="message" :message="message"
> >
<o-field expanded> <o-field class="!mt-0">
<o-input <o-input
expanded
class="!mt-0"
aria-required="true" aria-required="true"
required required
v-model="identity.preferredUsername" v-model="identity.preferredUsername"
@ -60,7 +60,7 @@
<o-field <o-field
horizontal horizontal
:label="$t('Description')" :label="t('Description')"
label-for="identity-summary" label-for="identity-summary"
> >
<o-input <o-input
@ -82,29 +82,29 @@
>{{ error }}</o-notification >{{ error }}</o-notification
> >
<o-field class="submit"> <o-field class="flex justify-center !my-6">
<div class="control"> <div class="control">
<o-button type="button" variant="primary" @click="submit()"> <o-button type="button" variant="primary" @click="submit()">
{{ $t("Save") }} {{ t("Save") }}
</o-button> </o-button>
</div> </div>
</o-field> </o-field>
<o-field class="delete-identity"> <o-field class="flex justify-center">
<o-button <o-button
v-if="isUpdate" v-if="isUpdate"
@click="openDeleteIdentityConfirmation()" @click="openDeleteIdentityConfirmation()"
variant="text" variant="text"
> >
{{ $t("Delete this identity") }} {{ t("Delete this identity") }}
</o-button> </o-button>
</o-field> </o-field>
<section v-if="isUpdate"> <section v-if="isUpdate">
<h2>{{ $t("Profile feeds") }}</h2> <h2>{{ t("Profile feeds") }}</h2>
<p> <p>
{{ {{
$t( t(
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings." "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings."
) )
}} }}
@ -116,7 +116,7 @@
:key="feedToken.token" :key="feedToken.token"
> >
<o-tooltip <o-tooltip
:label="$t('URL copied to clipboard')" :label="t('URL copied to clipboard')"
:active="showCopiedTooltip.atom" :active="showCopiedTooltip.atom"
always always
variant="success" variant="success"
@ -131,11 +131,11 @@
" "
:href="tokenToURL(feedToken.token, 'atom')" :href="tokenToURL(feedToken.token, 'atom')"
target="_blank" target="_blank"
>{{ $t("RSS/Atom Feed") }}</o-button >{{ t("RSS/Atom Feed") }}</o-button
> >
</o-tooltip> </o-tooltip>
<o-tooltip <o-tooltip
:label="$t('URL copied to clipboard')" :label="t('URL copied to clipboard')"
:active="showCopiedTooltip.ics" :active="showCopiedTooltip.ics"
always always
variant="success" variant="success"
@ -150,14 +150,14 @@
icon-left="calendar-sync" icon-left="calendar-sync"
:href="tokenToURL(feedToken.token, 'ics')" :href="tokenToURL(feedToken.token, 'ics')"
target="_blank" target="_blank"
>{{ $t("ICS/WebCal Feed") }}</o-button >{{ t("ICS/WebCal Feed") }}</o-button
> >
</o-tooltip> </o-tooltip>
<o-button <o-button
icon-left="refresh" icon-left="refresh"
variant="text" variant="text"
@click="openRegenerateFeedTokensConfirmation" @click="openRegenerateFeedTokensConfirmation"
>{{ $t("Regenerate new links") }}</o-button >{{ t("Regenerate new links") }}</o-button
> >
</div> </div>
</div> </div>
@ -166,7 +166,7 @@
icon-left="refresh" icon-left="refresh"
variant="text" variant="text"
@click="generateFeedTokens" @click="generateFeedTokens"
>{{ $t("Create new links") }}</o-button >{{ t("Create new links") }}</o-button
> >
</div> </div>
</section> </section>
@ -176,28 +176,14 @@
<style scoped lang="scss"> <style scoped lang="scss">
@use "@/styles/_mixins" as *; @use "@/styles/_mixins" as *;
h1 { // h1 {
display: flex; // display: flex;
justify-content: center; // justify-content: center;
} // }
.picture-upload { // .username-field + .field {
margin: 30px 0; // margin-bottom: 0;
} // }
.submit,
.delete-identity {
display: flex;
justify-content: center;
}
.submit {
margin: 30px 0;
}
.username-field + .field {
margin-bottom: 0;
}
:deep(.buttons > *:not(:last-child) .button) { :deep(.buttons > *:not(:last-child) .button) {
@include margin-right(0.5rem); @include margin-right(0.5rem);
@ -286,12 +272,12 @@ const baseIdentity: IPerson = {
suspended: false, suspended: false,
}; };
const identity = ref(baseIdentity); const identity = ref<IPerson>(baseIdentity);
watch(person, () => { watch(person, () => {
console.debug("person changed", person.value); console.debug("person changed", person.value);
if (person.value) { if (person.value) {
identity.value = person.value; identity.value = { ...person.value };
} }
}); });

View File

@ -14,20 +14,13 @@ import { useLazyQuery, useMutation } from "@vue/apollo-composable";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useHead } from "@vueuse/head"; import { useHead } from "@vueuse/head";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
import { getValueFromMeta } from "@/utils/html";
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
useHead({ useHead({
title: computed(() => t("Redirecting to Mobilizon")), title: computed(() => t("Redirecting to Mobilizon")),
}); });
const getValueFromMeta = (name: string): string | null => {
const element = document.querySelector(`meta[name="${name}"]`);
if (element && element.getAttribute("content")) {
return element.getAttribute("content");
}
return null;
};
const accessToken = getValueFromMeta("auth-access-token"); const accessToken = getValueFromMeta("auth-access-token");
const refreshToken = getValueFromMeta("auth-refresh-token"); const refreshToken = getValueFromMeta("auth-refresh-token");
const userId = getValueFromMeta("auth-user-id"); const userId = getValueFromMeta("auth-user-id");
@ -36,7 +29,10 @@ const userRole = getValueFromMeta("auth-user-role") as ICurrentUserRole;
const router = useRouter(); const router = useRouter();
const { onDone, mutate } = useMutation< const {
onDone: onUpdateCurrentUserClientDone,
mutate: updateCurrentUserClient,
} = useMutation<
{ updateCurrentUser: ICurrentUser }, { updateCurrentUser: ICurrentUser },
{ id: string; email: string; isLoggedIn: boolean; role: ICurrentUserRole } { id: string; email: string; isLoggedIn: boolean; role: ICurrentUserRole }
>(UPDATE_CURRENT_USER_CLIENT); >(UPDATE_CURRENT_USER_CLIENT);
@ -45,16 +41,19 @@ const { onResult: onLoggedUserResult, load: loadUser } = useLazyQuery<{
loggedUser: IUser; loggedUser: IUser;
}>(LOGGED_USER); }>(LOGGED_USER);
onDone(async () => { onUpdateCurrentUserClientDone(async () => {
loadUser(); loadUser();
onLoggedUserResult(async ({ data: { loggedUser } }) => { });
if (loggedUser.defaultActor) {
await changeIdentity(loggedUser.defaultActor); onLoggedUserResult(async (result) => {
await router.push({ name: RouteName.HOME }); if (result.loading) return;
} else { const loggedUser = result.data.loggedUser;
// No need to push to REGISTER_PROFILE, the navbar will do it for us if (loggedUser.defaultActor) {
} await changeIdentity(loggedUser.defaultActor);
}); await router.push({ name: RouteName.HOME });
} else {
// No need to push to REGISTER_PROFILE, the navbar will do it for us
}
}); });
onMounted(async () => { onMounted(async () => {
@ -67,13 +66,15 @@ onMounted(async () => {
email: userEmail, email: userEmail,
role: userRole, role: userRole,
isLoggedIn: true, isLoggedIn: true,
defaultActor: undefined,
actors: [],
}, },
accessToken, accessToken,
refreshToken, refreshToken,
}; };
saveUserData(login); saveUserData(login);
mutate({ updateCurrentUserClient({
id: userId, id: userId,
email: userEmail, email: userEmail,
isLoggedIn: true, isLoggedIn: true,

View File

@ -71,7 +71,9 @@ defmodule Mobilizon.Web.AuthController do
render(conn, "callback.html", %{ render(conn, "callback.html", %{
access_token: access_token, access_token: access_token,
refresh_token: refresh_token, refresh_token: refresh_token,
user: user user: user,
username: username_from_ueberauth(auth),
name: display_name_from_ueberauth(auth)
}) })
else else
err -> err ->
@ -114,6 +116,18 @@ defmodule Mobilizon.Web.AuthController do
defp email_from_ueberauth(_), do: nil defp email_from_ueberauth(_), do: nil
defp username_from_ueberauth(%Ueberauth.Auth{info: %Ueberauth.Auth.Info{nickname: nickname}})
when is_valid_string(nickname),
do: nickname
defp username_from_ueberauth(_), do: nil
defp display_name_from_ueberauth(%Ueberauth.Auth{info: %Ueberauth.Auth.Info{name: name}})
when is_valid_string(name),
do: name
defp display_name_from_ueberauth(_), do: nil
@spec provider_config(String.t()) :: {:ok, any()} | {:error, :not_supported | :unknown_error} @spec provider_config(String.t()) :: {:ok, any()} | {:error, :not_supported | :unknown_error}
defp provider_config(provider_name) do defp provider_config(provider_name) do
with ueberauth when is_list(ueberauth) <- Application.get_env(:ueberauth, Ueberauth), with ueberauth when is_list(ueberauth) <- Application.get_env(:ueberauth, Ueberauth),

View File

@ -20,7 +20,9 @@ defmodule Mobilizon.Web.AuthView do
email: user_email, email: user_email,
role: user_role, role: user_role,
default_actor_id: user_actor_id default_actor_id: user_actor_id
} },
username: username,
name: name
} = assigns } = assigns
) do ) do
info_tags = [ info_tags = [
@ -29,7 +31,9 @@ defmodule Mobilizon.Web.AuthView do
Tag.tag(:meta, name: "auth-user-id", content: user_id), Tag.tag(:meta, name: "auth-user-id", content: user_id),
Tag.tag(:meta, name: "auth-user-email", content: user_email), Tag.tag(:meta, name: "auth-user-email", content: user_email),
Tag.tag(:meta, name: "auth-user-role", content: String.upcase(to_string(user_role))), Tag.tag(:meta, name: "auth-user-role", content: String.upcase(to_string(user_role))),
Tag.tag(:meta, name: "auth-user-actor-id", content: user_actor_id) Tag.tag(:meta, name: "auth-user-actor-id", content: user_actor_id),
Tag.tag(:meta, name: "auth-user-suggested-actor-username", content: username),
Tag.tag(:meta, name: "auth-user-suggested-actor-name", content: name)
] ]
with tags <- Instance.build_tags() ++ info_tags, with tags <- Instance.build_tags() ++ info_tags,