Merge branch 'indieauth'

This commit is contained in:
Andrea Vos 2022-04-23 13:15:48 +02:00
commit cd50208c37
30 changed files with 320 additions and 45 deletions

View File

@ -1,16 +1,21 @@
<template> <template>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center"> <div class="d-flex flex-column flex-md-row justify-content-between align-items-center">
<span class="my-2"> <span class="my-2">
<Icon :v="providerOptions.icon || provider" set="b"/> <Icon :v="providerOptions.icon || provider" set="b"
:class="[providerOptions.icon && providerOptions.icon.endsWith('.png') ? 'mx-1 invertible' : '']"/>
{{ providerOptions.name }} {{ providerOptions.name }}
<button v-if="providerOptions.deprecated" class="badge bg-light text-dark border border-warning" @click="depreciationNotice(providerOptions.deprecated)">
<Icon v="exclamation-triangle"/>
<T>user.login.deprecated</T>
</button>
</span> </span>
<span v-if="connection === undefined"> <span v-if="connection === undefined">
<template v-if="providerOptions.instanceRequired"> <template v-if="providerOptions.instanceRequired">
<form v-if="formShown" <form v-if="formShown"
:action="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}`" :action="providerOptions.redirectViaHome ? `${homeUrl}/api/user/social-redirect/${provider}/${config.locale}` : `/api/connect/${provider}`"
class="input-group input-group-sm"> class="input-group input-group-sm">
<input type="text" name="instance" class="form-control" autofocus required <input type="text" name="instance" class="form-control" autofocus required
:placeholder="$t('user.login.instancePlaceholder')"/> :placeholder="$t(providerOptions.domain ? 'user.login.domainPlaceholder' : 'user.login.instancePlaceholder')"/>
<button type="submit" class="btn btn-outline-secondary"> <button type="submit" class="btn btn-outline-secondary">
<Icon v="link"/> <Icon v="link"/>
</button> </button>
@ -20,21 +25,21 @@
<T>user.socialConnection.connect</T> <T>user.socialConnection.connect</T>
</button> </button>
</template> </template>
<a v-else :href="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}`" class="badge bg-light text-dark border"> <a v-else :href="providerOptions.redirectViaHome ? `${homeUrl}/api/user/social-redirect/${provider}/${config.locale}` : `/api/connect/${provider}`" class="badge bg-light text-dark border">
<Icon v="link"/> <Icon v="link"/>
<T>user.socialConnection.connect</T> <T>user.socialConnection.connect</T>
</a> </a>
</span> </span>
<span v-else class="text-center"> <span v-else class="text-center">
<span class="mr-3"> <span class="me-2">
<a href="#" @click.prevent="$emit('setAvatar', provider)"> <a v-if="providerOptions.avatars && connection.avatar" href="#" @click.prevent="$emit('setAvatar', provider)">
<Avatar :src="connection.avatar" :user="$user()" dsize="2rem"/> <Avatar :src="connection.avatar" :user="$user()" dsize="2rem"/>
</a> </a>
{{connection.name}} {{connection.name}}
</span> </span>
<br class="d-md-none"/> <br class="d-md-none"/>
<a :href="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}` + (providerOptions.instanceRequired ? '?instance=' + connection.name.split('@')[1] : '')" <a :href="(providerOptions.redirectViaHome ? `${homeUrl}/api/user/social-redirect/${provider}/${config.locale}` : `/api/connect/${provider}`) + (providerOptions.instanceRequired ? '?instance=' + connection.name.split('@')[1] : '')"
class="badge bg-light text-dark border"> class="badge bg-light text-dark border">
<Icon v="sync"/> <Icon v="sync"/>
<T>user.socialConnection.refresh</T> <T>user.socialConnection.refresh</T>
</a> </a>
@ -48,31 +53,34 @@
</template> </template>
<script> <script>
export default { export default {
props: { props: {
provider: { required: true }, provider: { required: true },
providerOptions: { required: true }, providerOptions: { required: true },
connection: {}, connection: {},
}, },
data() { data() {
return { return {
disconnecting: false, disconnecting: false,
homeUrl: process.env.HOME_URL, homeUrl: process.env.HOME_URL,
formShown: false, formShown: false,
}
},
methods: {
async disconnect() {
await this.$confirm(this.$t('user.socialConnection.disconnectConfirm', {email: this.$user().email}), 'danger');
this.disconnecting = true;
try {
const response = await this.$post(`/user/social-connection/${this.provider}/disconnect`);
this.$emit('disconnected', response);
} finally {
this.disconnecting = false;
} }
}, },
methods: { async depreciationNotice(link) {
async disconnect() { await this.$alert(this.$t('user.login.depreciationNotice', {link}), 'warning');
await this.$confirm(this.$t('user.socialConnection.disconnectConfirm', {email: this.$user().email}), 'danger'); }
},
this.disconnecting = true; }
try {
const response = await this.$post(`/user/social-connection/${this.provider}/disconnect`);
this.$emit('disconnected', response);
} finally {
this.disconnecting = false;
}
},
},
}
</script> </script>

View File

@ -3,10 +3,10 @@
<Icon :v="options.icon || provider" set="b"/> <Icon :v="options.icon || provider" set="b"/>
{{ options.name }} {{ options.name }}
<form :action="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}`" <form :action="link"
v-if="options.instanceRequired" class="input-group my-2"> v-if="options.instanceRequired" class="input-group my-2">
<input type="text" name="instance" class="form-control" autofocus required ref="instance" <input type="text" name="instance" class="form-control" autofocus required ref="instance"
:placeholder="$t('user.login.instancePlaceholder')"> :placeholder="$t(options.domain ? 'user.login.domainPlaceholder' : 'user.login.instancePlaceholder')"/>
<button type="submit" class="btn btn-outline-primary"> <button type="submit" class="btn btn-outline-primary">
<Icon v="arrow-right"/> <Icon v="arrow-right"/>
</button> </button>
@ -15,11 +15,20 @@
<button v-else-if="options.instanceRequired && !formShown" <button v-else-if="options.instanceRequired && !formShown"
class="btn btn-outline-primary" class="btn btn-outline-primary"
@click="showForm"> @click="showForm">
<Icon :v="options.icon || provider" set="b"/> <Icon :v="options.icon || provider" set="b"
:class="[options.icon && options.icon.endsWith('.png') ? 'mx-1 invertible' : '']"/>
{{ options.name }} {{ options.name }}
</button> </button>
<a v-else :href="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}`" <a v-else-if="options.deprecated" :href="link"
class="btn btn-outline-primary"> class="btn btn-outline-secondary btn-sm"
@click.prevent="depreciationNotice(options.deprecated)"
>
<Icon :v="options.icon || provider" set="b"/>
{{ options.name }}
</a>
<a v-else :href="link"
class="btn btn-outline-primary"
>
<Icon :v="options.icon || provider" set="b"/> <Icon :v="options.icon || provider" set="b"/>
{{ options.name }} {{ options.name }}
</a> </a>
@ -37,11 +46,20 @@ export default {
formShown: false, formShown: false,
}; };
}, },
computed: {
link() {
return this.options.redirectViaHome ? `${this.homeUrl}/api/user/social-redirect/${this.provider}/${this.config.locale}` : `/api/connect/${this.provider}`
}
},
methods: { methods: {
showForm() { showForm() {
this.formShown = true; this.formShown = true;
this.$nextTick(() => this.$refs.instance.focus()); this.$nextTick(() => this.$refs.instance.focus());
} },
async depreciationNotice(link) {
await this.$confirm(this.$t('user.login.depreciationNotice', {link}), 'warning');
window.location.href = this.link;
},
} }
}; };
</script> </script>

View File

@ -497,6 +497,12 @@ user:
Registering lets you manage your cards ({/@example=like this one}). Registering lets you manage your cards ({/@example=like this one}).
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' instancePlaceholder: 'Instance'
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validate' action: 'Validate'
invalid: 'Invalid code.' invalid: 'Invalid code.'

View File

@ -421,6 +421,13 @@ user:
Mit der Registrierung kannst du deine Visitenkarten verwalten ({/@example=wie diese}). Mit der Registrierung kannst du deine Visitenkarten verwalten ({/@example=wie diese}).
passwordless: 'Die Website speichert keine Passwörter. {https://avris.it/blog/passwords-are-passé=Weitere Infos.}' passwordless: 'Die Website speichert keine Passwörter. {https://avris.it/blog/passwords-are-passé=Weitere Infos.}'
instancePlaceholder: 'Instanz' instancePlaceholder: 'Instanz'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validieren' action: 'Validieren'
invalid: 'Ungültiger Code.' invalid: 'Ungültiger Code.'

View File

@ -0,0 +1,23 @@
# We don't recommend using Facebook login
<small>2022-04-23 | [@andrea](/@andrea)</small>
We all know that Meta/Facebook is a company that couldn't give a shit about morality or users' privacy.
But did you know they're also unfriendly towards developers and try to impose their prudish, conservative views on independent websites?
This morning they've disabled two apps of mine whose whole purpose was simply to let people log in to a website using their Facebook account.
Nothing more, no actual dependency on Facebook, just the “log in with” button.
Why? One of them, [NakedAdventure](https://naked-adventure.eu/), is a map of nude beaches and contains small amounts of non-sexual nudity.
The other, [OurSong](https://oursong.eurovote.eu/), is a harmless game where people vote for their favourite songs
but it's only accessible for members, so Facebook didn't like the fact that they cannot sniff around.
They have blocked the apps without notice, making independent websites inaccessible for some users on a whim.
If they think it's okay to suddenly cut people off other websites just because there was some non-sexual nudity present
or because they weren't able to snoop around, who knows if one day they won't block Pronouns.page because of the queer content?
After all, in countries like Russia distributing information about LGBTQ+ issues is already considered adult content.
Users of Pronouns.page seem to dislike Facebook anyway only 2.76% of you has login with Facebook set up
(compared for example to 38,95% of people using login with Twitter).
But to those who use it as their primary login option
**we strongly recommend setting up a different login method and ditching Facebook**.

View File

@ -521,6 +521,12 @@ user:
Registering lets you manage your cards ({/@example=like this one}). Registering lets you manage your cards ({/@example=like this one}).
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' instancePlaceholder: 'Instance'
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validate' action: 'Validate'
invalid: 'Invalid code.' invalid: 'Invalid code.'

View File

@ -482,6 +482,13 @@ user:
Registering lets you manage your cards ({/@example=like this one}). Registering lets you manage your cards ({/@example=like this one}).
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' instancePlaceholder: 'Instance'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validate' action: 'Validate'
invalid: 'Nevalida kodo.' invalid: 'Nevalida kodo.'

View File

@ -506,6 +506,13 @@ user:
Registrarte te permite manejar tus tarjetas ({/@example=como esta}). Registrarte te permite manejar tus tarjetas ({/@example=como esta}).
passwordless: 'Este sitio web no guarda las contraseñas. {https://avris.it/blog/passwords-are-passé=Más información.}' passwordless: 'Este sitio web no guarda las contraseñas. {https://avris.it/blog/passwords-are-passé=Más información.}'
instancePlaceholder: 'Instancia' instancePlaceholder: 'Instancia'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validar' action: 'Validar'
invalid: 'Código inválido.' invalid: 'Código inválido.'

View File

@ -424,6 +424,13 @@ user:
Sinscrire vous permet de gérer vos cartes ({/@example=comme celle-ci}). Sinscrire vous permet de gérer vos cartes ({/@example=comme celle-ci}).
passwordless: 'Ce site ne stocke aucun mot de passe. {https://avris.it/blog/passwords-are-passé=Plus dinfos.}' passwordless: 'Ce site ne stocke aucun mot de passe. {https://avris.it/blog/passwords-are-passé=Plus dinfos.}'
instancePlaceholder: 'Instance' instancePlaceholder: 'Instance'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Valider' action: 'Valider'
invalid: 'Code invalide.' invalid: 'Code invalide.'

View File

@ -405,6 +405,13 @@ user:
Registrar-se te permite dirigir os cartões ({/@example=como esta}). Registrar-se te permite dirigir os cartões ({/@example=como esta}).
passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' # TODO instancePlaceholder: 'Instance' # TODO
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validar' action: 'Validar'
invalid: 'Código inválido.' invalid: 'Código inválido.'

View File

@ -498,6 +498,13 @@ user:
Registering lets you manage your cards ({/@example=like this one}). Registering lets you manage your cards ({/@example=like this one}).
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' instancePlaceholder: 'Instance'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validate' action: 'Validate'
invalid: 'Invalid code.' invalid: 'Invalid code.'

View File

@ -426,6 +426,13 @@ user:
ご登録いただいた方は、カードの設定を行うことができます。({/@example=カードの例}). ご登録いただいた方は、カードの設定を行うことができます。({/@example=カードの例}).
passwordless: 'このウェブサイトはパスワードを保存しません。 {https://avris.it/blog/passwords-are-passé=詳細はこちら。}' passwordless: 'このウェブサイトはパスワードを保存しません。 {https://avris.it/blog/passwords-are-passé=詳細はこちら。}'
instancePlaceholder: 'サーバー' instancePlaceholder: 'サーバー'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: '確認' action: '確認'
invalid: '無効なコード' invalid: '無効なコード'

View File

@ -495,6 +495,13 @@ user:
등록하면 카드를 ({/@example=이렇게}) 관리할 수 있습니다. 등록하면 카드를 ({/@example=이렇게}) 관리할 수 있습니다.
passwordless: '웹사이트는 비밀번호를 저장하지 않습니다. {https://avris.it/blog/passwords-are-passé=추가 정보.}' passwordless: '웹사이트는 비밀번호를 저장하지 않습니다. {https://avris.it/blog/passwords-are-passé=추가 정보.}'
instancePlaceholder: '사례' instancePlaceholder: '사례'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: '확인' action: '확인'
invalid: '유효하지 않은 코드' invalid: '유효하지 않은 코드'

View File

@ -402,6 +402,13 @@ user:
Rejistrarte te permete kontentarte tus kartas ({/@example=komo esta}). Rejistrarte te permete kontentarte tus kartas ({/@example=komo esta}).
passwordless: 'Este sitio gueb no guadra las kontrasenyas. {https://avris.it/blog/passwords-are-passé=Mas informasion.}' passwordless: 'Este sitio gueb no guadra las kontrasenyas. {https://avris.it/blog/passwords-are-passé=Mas informasion.}'
instancePlaceholder: 'Instansia' instancePlaceholder: 'Instansia'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Balidar' action: 'Balidar'
invalid: 'Kodiche enfirmo.' invalid: 'Kodiche enfirmo.'

View File

@ -424,6 +424,13 @@ user:
Door te registreren kun je een kaart ({/@example=zoals deze}) maken. Door te registreren kun je een kaart ({/@example=zoals deze}) maken.
passwordless: 'De website slaat geen wachtwoorden op. {https://avris.it/blog/passwords-are-passé=Meer info.}' passwordless: 'De website slaat geen wachtwoorden op. {https://avris.it/blog/passwords-are-passé=Meer info.}'
instancePlaceholder: 'Instantie' instancePlaceholder: 'Instantie'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Valideer' action: 'Valideer'
invalid: 'Ongeldige code.' invalid: 'Ongeldige code.'

View File

@ -401,6 +401,13 @@ user:
Å registrere seg lar deg redigere kortene dine ({/@example=sånn som denne}). Å registrere seg lar deg redigere kortene dine ({/@example=sånn som denne}).
passwordless: 'Denne nettsiden lagrer ingen passord. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'Denne nettsiden lagrer ingen passord. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Forekomst' instancePlaceholder: 'Forekomst'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Gyldig' action: 'Gyldig'
invalid: 'Ugyldig kode.' invalid: 'Ugyldig kode.'

View File

@ -1196,6 +1196,12 @@ user:
Założenie konta pozwala na zarządzanie swoimi wizytówkami ({/@example=takimi jak ta}). Założenie konta pozwala na zarządzanie swoimi wizytówkami ({/@example=takimi jak ta}).
passwordless: 'Strona nie zapisuje żadnych haseł. {https://avris.it/blog/passwords-are-passé=Więcej info.}' passwordless: 'Strona nie zapisuje żadnych haseł. {https://avris.it/blog/passwords-are-passé=Więcej info.}'
instancePlaceholder: 'Instancja' instancePlaceholder: 'Instancja'
domainPlaceholder: 'Domena'
deprecated: 'Odradzane'
depreciationNotice: >
Ten dostawca uwierzytelniania nie jest godny zaufania.
<a href="%link%" target="_blank" rel="noopener">Więcej szczegółów pod tym linkiem</a>.
Mocno polecamy włączenie innej metody uwierzytelniania.
code: code:
action: 'Sprawdź' action: 'Sprawdź'
invalid: 'Kod nieprawidłowy.' invalid: 'Kod nieprawidłowy.'

View File

@ -427,6 +427,13 @@ user:
Registrar-se te permite dirigir os cartões ({/@example=como esta}). Registrar-se te permite dirigir os cartões ({/@example=como esta}).
passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instância' instancePlaceholder: 'Instância'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validar' action: 'Validar'
invalid: 'Código inválido.' invalid: 'Código inválido.'

View File

@ -518,6 +518,13 @@ user:
Регистрация позволяет вам управлять своими аккаунтами/карточками ({/@exemple=как, например, этой}). Регистрация позволяет вам управлять своими аккаунтами/карточками ({/@exemple=как, например, этой}).
passwordless: 'Сайт не хранит пароли. {https://avris.it/blog/passwords-are-passé=Больше информации}' passwordless: 'Сайт не хранит пароли. {https://avris.it/blog/passwords-are-passé=Больше информации}'
instancePlaceholder: 'инстанция' instancePlaceholder: 'инстанция'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Подтвердить' action: 'Подтвердить'
invalid: 'Неверный код.' invalid: 'Неверный код.'

View File

@ -476,6 +476,13 @@ user:
Registering låter dig hantera dina kort ({/@andrea=som det här}). Registering låter dig hantera dina kort ({/@andrea=som det här}).
passwordless: 'Webbplatsen lagrar inga lösenord. {https://avris.it/blog/passwords-are-passé=Mer information.}' passwordless: 'Webbplatsen lagrar inga lösenord. {https://avris.it/blog/passwords-are-passé=Mer information.}'
instancePlaceholder: 'Instans' instancePlaceholder: 'Instans'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Bekräfta' action: 'Bekräfta'
invalid: 'Ogiltig kod.' invalid: 'Ogiltig kod.'

View File

@ -541,6 +541,13 @@ user:
Реєстрація дозволяє вам управляти своїми акаунтами/картками ({/@exemple=як, наприклад, цією}). Реєстрація дозволяє вам управляти своїми акаунтами/картками ({/@exemple=як, наприклад, цією}).
passwordless: 'Сайт не зберігає паролі. {https://avris.it/blog/passwords-are-passé=Більше інформації}' passwordless: 'Сайт не зберігає паролі. {https://avris.it/blog/passwords-are-passé=Більше інформації}'
instancePlaceholder: 'інстанція' instancePlaceholder: 'інстанція'
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Підтвердити' action: 'Підтвердити'
invalid: 'Невірний код.' invalid: 'Невірний код.'

View File

@ -395,6 +395,13 @@ user:
Registering lets you manage your cards ({/@example=like this one}). Registering lets you manage your cards ({/@example=like this one}).
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}' passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
instancePlaceholder: 'Instance' # TODO instancePlaceholder: 'Instance' # TODO
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: 'Validate' action: 'Validate'
invalid: 'Invalid code.' invalid: 'Invalid code.'

View File

@ -380,6 +380,13 @@ user:
註冊可以讓你管理你的卡({/@example=像這個})。 註冊可以讓你管理你的卡({/@example=像這個})。
passwordless: '該網站不存儲任何密碼。 {https://avris.it/blog/passwords-are-passé=更多信息。}' passwordless: '該網站不存儲任何密碼。 {https://avris.it/blog/passwords-are-passé=更多信息。}'
instancePlaceholder: 'Instance' # TODO instancePlaceholder: 'Instance' # TODO
# TODO
domainPlaceholder: 'Domain'
deprecated: 'Deprecated'
depreciationNotice: >
This is not a reliable authentication provider.
<a href="%link%" target="_blank" rel="noopener">Check out this link for more details</a>.
We highly recommend making sure that you have a different login method available.
code: code:
action: '證實' action: '證實'
invalid: '不對代碼' invalid: '不對代碼'

View File

@ -42,6 +42,7 @@
"nuxt": "^2.15.2", "nuxt": "^2.15.2",
"pageres": "^6.2.3", "pageres": "^6.2.3",
"qrcode": "^1.5.0", "qrcode": "^1.5.0",
"query-string": "^7.1.1",
"rtlcss": "^3.1.2", "rtlcss": "^3.1.2",
"sha1": "^1.1.1", "sha1": "^1.1.1",
"speakeasy": "^2.0.0", "speakeasy": "^2.0.0",

View File

@ -6,17 +6,25 @@ import SQL from 'sql-template-strings';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import assert from 'assert'; import assert from 'assert';
import { handleErrorAsync } from "../../src/helpers"; import { handleErrorAsync } from "../../src/helpers";
import queryString from 'query-string';
const normalizeDomainName = (domain) => { const normalizeDomainName = (domain) => {
const url = new URL('https://' + domain); const url = new URL('https://' + domain.replace(/^https?:\/\//, ''));
assert(url.port === ''); assert(url.port === '');
return url.hostname; return url.hostname;
} }
const baseUrl = process.env.BASE_URL || process.env.HOME_URL || 'https://pronouns.page';
const config = { const config = {
mastodon: { mastodon: {
scopes: ['read:accounts'], scopes: ['read:accounts'],
redirect_uri: `${process.env.HOME_URL || 'https://pronouns.page'}/api/user/social/mastodon`, redirect_uri: `${baseUrl}/api/user/social/mastodon`,
},
indieauth: {
server_uri: 'https://indieauth.com/auth',
client_id: baseUrl,
redirect_uri: `${baseUrl}/api/user/social/indieauth`,
}, },
}; };
@ -52,6 +60,7 @@ const mastodonGetOAuthKeys = async (db, instance) => {
`); `);
return keys; return keys;
}; };
router.get('/connect/mastodon', handleErrorAsync(async (req, res) => { router.get('/connect/mastodon', handleErrorAsync(async (req, res) => {
assert(req.query.instance); assert(req.query.instance);
const instance = normalizeDomainName(req.query.instance); const instance = normalizeDomainName(req.query.instance);
@ -64,6 +73,7 @@ router.get('/connect/mastodon', handleErrorAsync(async (req, res) => {
response_type: 'code', response_type: 'code',
})); }));
})); }));
router.get('/user/social/mastodon', handleErrorAsync(async (req, res, next) => { router.get('/user/social/mastodon', handleErrorAsync(async (req, res, next) => {
if (!req.session.grant || !req.session.grant.instance || !req.query.code) { if (!req.session.grant || !req.session.grant.instance || !req.query.code) {
next(); next();
@ -99,7 +109,47 @@ router.get('/user/social/mastodon', handleErrorAsync(async (req, res, next) => {
response.instance = instance; response.instance = instance;
req.session.grant.response = response; req.session.grant.response = response;
next(); next();
return; }));
router.get('/connect/indieauth', handleErrorAsync(async (req, res) => {
assert(req.query.instance);
const instance = normalizeDomainName(req.query.instance);
req.session.grant = { instance };
res.redirect(`${config.indieauth.server_uri}?` + new URLSearchParams({
me: `https://${instance}`,
client_id: config.indieauth.client_id,
redirect_uri: config.indieauth.redirect_uri,
}));
}));
router.get('/user/social/indieauth', handleErrorAsync(async (req, res, next) => {
if (!req.session.grant || !req.session.grant.instance || !req.query.code) { next(); return; }
const response = await fetch(config.indieauth.server_uri, {
method: 'POST',
body: new URLSearchParams({
code: req.query.code,
client_id: config.indieauth.client_id,
redirect_uri: config.indieauth.redirect_uri,
}).toString(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'pronouns.page',
},
}).then(res => res.text());
const profile = queryString.parse(response);
if (!profile || !profile.me) { next(); return; }
profile.domain = normalizeDomainName(profile.me);
req.session.grant.response = {
profile,
instance: req.session.grant.instance,
access_token: true,
};
next();
})); }));
export default router; export default router;

View File

@ -32,6 +32,7 @@ module.exports.config = {
}, },
// non-grant, but things break if it's not there // non-grant, but things break if it's not there
mastodon: {}, mastodon: {},
indieauth: {},
} }
module.exports.handlers = { module.exports.handlers = {
@ -84,6 +85,15 @@ module.exports.handlers = {
name: acct, name: acct,
avatar: r.profile.avatar, avatar: r.profile.avatar,
access_token: r.access_token, access_token: r.access_token,
instance: r.instance,
}; };
}, },
indieauth(r) {
return {
id: r.profile.me,
email: 'indieauth@' + r.profile.domain,
name: r.profile.domain,
instance: r.instance,
}
},
}; };

View File

@ -3,18 +3,36 @@ export const socialProviders = {
name: 'Mastodon', name: 'Mastodon',
instanceRequired: true, instanceRequired: true,
linkRegex: (p) => `^https?://(?:www.)?${p.name.split('@')[1]}/(?:(?:web/)?@|users/)?${p.name.split('@')[0]}/?$`, linkRegex: (p) => `^https?://(?:www.)?${p.name.split('@')[1]}/(?:(?:web/)?@|users/)?${p.name.split('@')[0]}/?$`,
avatars: true,
},
indieauth: {
name: 'IndieAuth',
instanceRequired: true,
domain: true,
icon: 'indieauth.png',
iconMargin: true,
avatars: false,
}, },
twitter: { twitter: {
name: 'Twitter', name: 'Twitter',
linkRegex: (p) => `^https?://(?:www.)?twitter.com/${p.name}/?$`, linkRegex: (p) => `^https?://(?:www.)?twitter.com/${p.name}/?$`,
redirectViaHome: true,
avatars: true,
}, },
discord: { discord: {
name: 'Discord', name: 'Discord',
}, redirectViaHome: true,
facebook: { avatars: true,
name: 'Facebook',
}, },
google: { google: {
name: 'Google', name: 'Google',
redirectViaHome: true,
avatars: true,
},
facebook: {
name: 'Facebook',
redirectViaHome: true,
avatars: false,
deprecated: 'https://en.pronouns.page/blog/facebook-login-deprecated',
}, },
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
static/img/indieauth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -8467,6 +8467,16 @@ query-string@^6.13.8:
split-on-first "^1.0.0" split-on-first "^1.0.0"
strict-uri-encode "^2.0.0" strict-uri-encode "^2.0.0"
query-string@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1"
integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==
dependencies:
decode-uri-component "^0.2.0"
filter-obj "^1.1.0"
split-on-first "^1.0.0"
strict-uri-encode "^2.0.0"
querystring-es3@^0.2.0: querystring-es3@^0.2.0:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"