parent
686a709e87
commit
ca0d53ec5d
5 changed files with 207 additions and 160 deletions
|
@ -8,6 +8,15 @@
|
||||||
|
|
||||||
You should also include the user name that made the change.
|
You should also include the user name that made the change.
|
||||||
-->
|
-->
|
||||||
|
## 13.x.x (unreleased)
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- 非ログイン時にMiAuthを踏んだ際にMiAuthであることを表示する
|
||||||
|
- /auth/のUIをアップデート
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
-
|
||||||
|
|
||||||
## 13.5.4 (2023/02/09)
|
## 13.5.4 (2023/02/09)
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
|
@ -1628,12 +1628,14 @@ _permissions:
|
||||||
"write:gallery-likes": "ギャラリーのいいねを操作する"
|
"write:gallery-likes": "ギャラリーのいいねを操作する"
|
||||||
|
|
||||||
_auth:
|
_auth:
|
||||||
|
shareAccessTitle: "アプリへのアクセス許可"
|
||||||
shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
|
shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
|
||||||
shareAccessAsk: "アカウントへのアクセスを許可しますか?"
|
shareAccessAsk: "アカウントへのアクセスを許可しますか?"
|
||||||
permissionAsk: "このアプリは次の権限を要求しています"
|
permissionAsk: "このアプリは次の権限を要求しています"
|
||||||
pleaseGoBack: "アプリケーションに戻ってやっていってください"
|
pleaseGoBack: "アプリケーションに戻ってやっていってください"
|
||||||
callback: "アプリケーションに戻っています"
|
callback: "アプリケーションに戻っています"
|
||||||
denied: "アクセスを拒否しました"
|
denied: "アクセスを拒否しました"
|
||||||
|
pleaseLogin: "アプリケーションにアクセス許可を与えるには、ログインが必要です。"
|
||||||
|
|
||||||
_antennaSources:
|
_antennaSources:
|
||||||
all: "全てのノート"
|
all: "全てのノート"
|
||||||
|
|
|
@ -1,60 +1,66 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="">
|
<section>
|
||||||
<div class="">{{ $t('_auth.shareAccess', { name: app.name }) }}</div>
|
<div v-if="app.permission.length > 0">
|
||||||
<div class="">
|
<p>{{ i18n.ts._auth.permissionAsk }}</p>
|
||||||
<h2>{{ app.name }}</h2>
|
<ul>
|
||||||
<p class="id">{{ app.id }}</p>
|
<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
|
||||||
<p class="description">{{ app.description }}</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div>
|
||||||
<h2>{{ $ts._auth.permissionAsk }}</h2>
|
<div :class="$style.buttons">
|
||||||
<ul>
|
<MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||||
<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
|
<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
<div class="">
|
|
||||||
<MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton>
|
|
||||||
<MkButton inline primary @click="accept">{{ $ts.accept }}</MkButton>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
import { AuthSession } from 'misskey-js/built/entities';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
session: AuthSession;
|
||||||
MkButton,
|
}>();
|
||||||
},
|
|
||||||
props: ['session'],
|
|
||||||
computed: {
|
|
||||||
name(): string {
|
|
||||||
const el = document.createElement('div');
|
|
||||||
el.textContent = this.app.name;
|
|
||||||
return el.innerHTML;
|
|
||||||
},
|
|
||||||
app(): any {
|
|
||||||
return this.session.app;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
cancel() {
|
|
||||||
os.api('auth/deny', {
|
|
||||||
token: this.session.token,
|
|
||||||
}).then(() => {
|
|
||||||
this.$emit('denied');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
accept() {
|
const emit = defineEmits<{
|
||||||
os.api('auth/accept', {
|
(event: 'accepted'): void;
|
||||||
token: this.session.token,
|
(event: 'denied'): void;
|
||||||
}).then(() => {
|
}>();
|
||||||
this.$emit('accepted');
|
|
||||||
});
|
const app = $computed(() => props.session.app);
|
||||||
},
|
|
||||||
},
|
const name = $computed(() => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.textContent = app.name;
|
||||||
|
return el.innerHTML;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
os.api('auth/deny', {
|
||||||
|
token: props.session.token,
|
||||||
|
}).then(() => {
|
||||||
|
emit('denied');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function accept() {
|
||||||
|
os.api('auth/accept', {
|
||||||
|
token: props.session.token,
|
||||||
|
}).then(() => {
|
||||||
|
emit('accepted');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.buttons {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,93 +1,105 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="$i && fetching" class="">
|
<MkStickyContainer>
|
||||||
<MkLoading/>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
|
||||||
</div>
|
<MkSpacer :content-max="500">
|
||||||
<div v-else-if="$i">
|
<div v-if="state == 'fetch-session-error'">
|
||||||
<XForm
|
<p>{{ i18n.ts.somethingHappened }}</p>
|
||||||
v-if="state == 'waiting'"
|
</div>
|
||||||
ref="form"
|
<div v-else-if="$i && !session">
|
||||||
class="form"
|
<MkLoading />
|
||||||
:session="session"
|
</div>
|
||||||
@denied="state = 'denied'"
|
<div v-else-if="$i && session">
|
||||||
@accepted="accepted"
|
<XForm
|
||||||
/>
|
v-if="state == 'waiting'"
|
||||||
<div v-if="state == 'denied'" class="denied">
|
class="form"
|
||||||
<h1>{{ $ts._auth.denied }}</h1>
|
:session="session"
|
||||||
</div>
|
@denied="state = 'denied'"
|
||||||
<div v-if="state == 'accepted'" class="accepted">
|
@accepted="accepted"
|
||||||
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : $ts.allowed }}</h1>
|
/>
|
||||||
<p v-if="session.app.callbackUrl">{{ $ts._auth.callback }}<MkEllipsis/></p>
|
<div v-if="state == 'denied'">
|
||||||
<p v-if="!session.app.callbackUrl">{{ $ts._auth.pleaseGoBack }}</p>
|
<h1>{{ i18n.ts._auth.denied }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="state == 'fetch-session-error'" class="error">
|
<div v-if="state == 'accepted' && session">
|
||||||
<p>{{ $ts.somethingHappened }}</p>
|
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
|
||||||
</div>
|
<p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }}
|
||||||
</div>
|
<MkEllipsis />
|
||||||
<div v-else class="signin">
|
</p>
|
||||||
<MkSignin @login="onLogin"/>
|
<p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
|
||||||
|
<MkSignin @login="onLogin" />
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import XForm from './auth.form.vue';
|
import XForm from './auth.form.vue';
|
||||||
import MkSignin from '@/components/MkSignin.vue';
|
import MkSignin from '@/components/MkSignin.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { login } from '@/account';
|
import { $i, login } from '@/account';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
import { AuthSession } from 'misskey-js/built/entities';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
token: string;
|
||||||
XForm,
|
}>();
|
||||||
MkSignin,
|
|
||||||
},
|
|
||||||
props: ['token'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
state: null,
|
|
||||||
session: null,
|
|
||||||
fetching: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (!this.$i) return;
|
|
||||||
|
|
||||||
// Fetch session
|
let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
|
||||||
os.api('auth/session/show', {
|
let session = $ref<AuthSession | null>(null);
|
||||||
token: this.token,
|
|
||||||
}).then(session => {
|
|
||||||
this.session = session;
|
|
||||||
this.fetching = false;
|
|
||||||
|
|
||||||
// 既に連携していた場合
|
function accepted() {
|
||||||
if (this.session.app.isAuthorized) {
|
state = 'accepted';
|
||||||
os.api('auth/accept', {
|
if (session && session.app.callbackUrl) {
|
||||||
token: this.session.token,
|
const url = new URL(session.app.callbackUrl);
|
||||||
}).then(() => {
|
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
|
||||||
this.accepted();
|
location.href = `${session.app.callbackUrl}?token=${session.token}`;
|
||||||
});
|
}
|
||||||
} else {
|
}
|
||||||
this.state = 'waiting';
|
|
||||||
}
|
function onLogin(res) {
|
||||||
}).catch(error => {
|
login(res.i);
|
||||||
this.state = 'fetch-session-error';
|
}
|
||||||
this.fetching = false;
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!$i) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
session = await os.api('auth/session/show', {
|
||||||
|
token: props.token,
|
||||||
});
|
});
|
||||||
},
|
|
||||||
methods: {
|
// 既に連携していた場合
|
||||||
accepted() {
|
if (session.app.isAuthorized) {
|
||||||
this.state = 'accepted';
|
await os.api('auth/accept', {
|
||||||
if (this.session.app.callbackUrl) {
|
token: session.token,
|
||||||
const url = new URL(this.session.app.callbackUrl);
|
});
|
||||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
|
accepted();
|
||||||
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`;
|
} else {
|
||||||
}
|
state = 'waiting';
|
||||||
}, onLogin(res) {
|
}
|
||||||
login(res.i);
|
} catch (e) {
|
||||||
},
|
state = 'fetch-session-error';
|
||||||
},
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata({
|
||||||
|
title: i18n.ts._auth.shareAccessTitle,
|
||||||
|
icon: 'ti ti-apps',
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
|
.loginMessage {
|
||||||
|
text-align: center;
|
||||||
|
margin: 8px 0 24px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,41 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="800">
|
<MkStickyContainer>
|
||||||
<div v-if="$i">
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
|
||||||
<div v-if="state == 'waiting'" class="waiting">
|
<MkSpacer :content-max="800">
|
||||||
<div class="">
|
<div v-if="$i">
|
||||||
|
<div v-if="state == 'waiting'">
|
||||||
<MkLoading/>
|
<MkLoading/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-if="state == 'denied'">
|
||||||
<div v-if="state == 'denied'" class="denied">
|
|
||||||
<div class="">
|
|
||||||
<p>{{ i18n.ts._auth.denied }}</p>
|
<p>{{ i18n.ts._auth.denied }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-else-if="state == 'accepted'" class="accepted">
|
||||||
<div v-else-if="state == 'accepted'" class="accepted">
|
|
||||||
<div class="">
|
|
||||||
<p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p>
|
<p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p>
|
||||||
<p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p>
|
<p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-else>
|
||||||
<div v-else class="">
|
<div v-if="name">{{ $t('_auth.shareAccess', { name: name }) }}</div>
|
||||||
<div v-if="name" class="">{{ $t('_auth.shareAccess', { name: name }) }}</div>
|
<div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div>
|
||||||
<div v-else class="">{{ i18n.ts._auth.shareAccessAsk }}</div>
|
<div v-if="_permissions.length > 0">
|
||||||
<div class="">
|
<p>{{ i18n.ts._auth.permissionAsk }}</p>
|
||||||
<p>{{ i18n.ts._auth.permissionAsk }}</p>
|
<ul>
|
||||||
<ul>
|
<li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li>
|
||||||
<li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li>
|
</ul>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
<div :class="$style.buttons">
|
||||||
<div class="">
|
<MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton>
|
||||||
<MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton>
|
<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
|
||||||
<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-else>
|
||||||
<div v-else class="signin">
|
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
|
||||||
<MkSignin @login="onLogin"/>
|
<MkSignin @login="onLogin"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -45,6 +43,7 @@ import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { $i, login } from '@/account';
|
import { $i, login } from '@/account';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
session: string;
|
session: string;
|
||||||
|
@ -54,7 +53,7 @@ const props = defineProps<{
|
||||||
permission: string; // コンマ区切り
|
permission: string; // コンマ区切り
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const _permissions = props.permission.split(',');
|
const _permissions = props.permission ? props.permission.split(',') : [];
|
||||||
|
|
||||||
let state = $ref<string | null>(null);
|
let state = $ref<string | null>(null);
|
||||||
|
|
||||||
|
@ -83,8 +82,27 @@ function deny(): void {
|
||||||
function onLogin(res): void {
|
function onLogin(res): void {
|
||||||
login(res.i);
|
login(res.i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata({
|
||||||
|
title: 'MiAuth',
|
||||||
|
icon: 'ti ti-apps',
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
|
.buttons {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginMessage {
|
||||||
|
text-align: center;
|
||||||
|
margin: 8px 0 24px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue