Add error screen
This commit is contained in:
parent
800e545ff5
commit
3c1f07f3b0
7 changed files with 217 additions and 107 deletions
|
@ -18,17 +18,28 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
|
|||
) => {
|
||||
if (!isObject(request.query)) {
|
||||
server.log.warn('Request query is not an object');
|
||||
throw server.httpErrors.badRequest();
|
||||
await reply.redirect(urlJoin(
|
||||
websitePrefix,
|
||||
`/prompt-error?code=invalid_request&description=${
|
||||
encodeURIComponent('Request query is not an object')
|
||||
}`,
|
||||
));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
validateParam('client_id', request.query.client_id);
|
||||
validateParam('redirect_uri', request.query.redirect_uri);
|
||||
} catch (error) {
|
||||
if (error instanceof ParamError) {
|
||||
throw server.httpErrors.badRequest(error.message);
|
||||
await reply.redirect(urlJoin(
|
||||
websitePrefix,
|
||||
`/prompt-error?code=invalid_request&description=${encodeURIComponent(error.message)}`,
|
||||
));
|
||||
return;
|
||||
}
|
||||
server.log.error(error);
|
||||
throw server.httpErrors.internalServerError();
|
||||
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=internal'));
|
||||
return;
|
||||
}
|
||||
|
||||
const application = await database.applicationRepo.findOne({
|
||||
|
@ -36,8 +47,14 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
|
|||
clientId: request.query.client_id,
|
||||
},
|
||||
});
|
||||
if (application === undefined) throw server.httpErrors.badRequest('Unknown application');
|
||||
if (!application.redirectUris.includes(request.query.redirect_uri)) throw server.httpErrors.badRequest('Redirect URI not registered');
|
||||
if (application === undefined) {
|
||||
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=unknown_application'));
|
||||
return;
|
||||
}
|
||||
if (!application.redirectUris.includes(request.query.redirect_uri)) {
|
||||
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=unknown_redirect_uri'));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
validateParam('response_type', request.query.response_type);
|
||||
|
|
|
@ -23,5 +23,11 @@ module.exports = {
|
|||
'max-len': ['off']
|
||||
},
|
||||
},
|
||||
{
|
||||
files: '**/*.vue',
|
||||
rules: {
|
||||
'class-methods-use-this': ['off']
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
|
|
35
website/src/compontents/dialog-app.vue
Normal file
35
website/src/compontents/dialog-app.vue
Normal file
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<v-app class="dialog-app">
|
||||
<div>
|
||||
<div class="text-h4 text-center mt-8 mx-2">
|
||||
<span class="primary--text">Wulkanowy</span> Bridge
|
||||
</div>
|
||||
</div>
|
||||
<v-main class="px-4">
|
||||
<v-sheet max-width="500" class="mx-auto mt-16" color="transparent">
|
||||
<slot />
|
||||
</v-sheet>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component({
|
||||
name: 'DialogApp',
|
||||
})
|
||||
export default class DialogApp extends Vue {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dialog-app {
|
||||
background-color: #f7f7f7 !important;
|
||||
|
||||
.v-card__text, .v-card__title {
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,107 +1,96 @@
|
|||
<template>
|
||||
<v-app class="authenticate-prompt-app">
|
||||
<div>
|
||||
<div class="text-h4 text-center mt-8">
|
||||
<span class="primary--text">Wulkanowy</span> Bridge
|
||||
</div>
|
||||
<dialog-app class="authenticate-prompt-app">
|
||||
<v-alert type="error" text v-if="!promptId">
|
||||
Brak wymaganego parametru <code>prompt_id</code>
|
||||
</v-alert>
|
||||
<v-alert type="error" text v-else-if="promptInfoError">
|
||||
Nie udało się wczytać danych
|
||||
<template #append>
|
||||
<v-btn text color="error" @click="loadPromptInfo">
|
||||
Spróbuj ponownie
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-alert>
|
||||
<div class="d-flex align-center justify-center mt-16 mb-16" v-else-if="!promptInfo">
|
||||
<v-progress-circular indeterminate :size="96" color="primary" />
|
||||
</div>
|
||||
<v-main class="px-4">
|
||||
<v-sheet max-width="500" class="mx-auto mt-16" color="transparent">
|
||||
<v-alert type="error" text v-if="!promptId">
|
||||
Brak wymaganego parametru <code>prompt_id</code>
|
||||
</v-alert>
|
||||
<v-alert type="error" text v-else-if="promptInfoError">
|
||||
Nie udało się wczytać danych
|
||||
<template #append>
|
||||
<v-btn text color="error" @click="loadPromptInfo">
|
||||
Spróbuj ponownie
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-alert>
|
||||
<div class="d-flex align-center justify-center mt-16 mb-16" v-else-if="!promptInfo">
|
||||
<v-progress-circular indeterminate :size="96" color="primary" />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="pb-1 text--secondary">
|
||||
Krok <span class="primary--text">{{ step }}/3</span>
|
||||
</div>
|
||||
<v-card outlined>
|
||||
<div class="d-flex justify-center mn-16 avatar__wrapper">
|
||||
<v-badge
|
||||
:color="promptInfo.application.verified ? 'green' : 'grey'"
|
||||
offset-x="64"
|
||||
offset-y="16"
|
||||
bottom
|
||||
:content="promptInfo.application.verified ? 'Zweryfikowana' : 'Niezweryfikowana'"
|
||||
:value="step === 1"
|
||||
<template v-else>
|
||||
<div class="pb-1 text--secondary">
|
||||
Krok <span class="primary--text">{{ step }}/3</span>
|
||||
</div>
|
||||
<v-card outlined>
|
||||
<div class="d-flex justify-center mn-16 avatar__wrapper">
|
||||
<v-badge
|
||||
:color="promptInfo.application.verified ? 'green' : 'grey'"
|
||||
offset-x="64"
|
||||
offset-y="16"
|
||||
bottom
|
||||
:content="promptInfo.application.verified ? 'Zweryfikowana' : 'Niezweryfikowana'"
|
||||
:value="step === 1"
|
||||
>
|
||||
<transition name="scale">
|
||||
<v-sheet
|
||||
v-if="step === 1"
|
||||
width="128"
|
||||
height="128"
|
||||
class="avatar-sheet mx-4 overflow-hidden"
|
||||
outlined
|
||||
>
|
||||
<transition name="scale">
|
||||
<v-sheet
|
||||
v-if="step === 1"
|
||||
width="128"
|
||||
height="128"
|
||||
class="avatar-sheet mx-4 overflow-hidden"
|
||||
outlined
|
||||
<v-sheet
|
||||
class="fill-height d-flex align-center justify-center"
|
||||
:color="promptInfo.application.iconColor"
|
||||
>
|
||||
<v-img
|
||||
:src="promptInfo.application.iconUrl"
|
||||
width="80"
|
||||
height="80"
|
||||
aspect-ratio="1"
|
||||
contain
|
||||
>
|
||||
<v-sheet
|
||||
class="fill-height d-flex align-center justify-center"
|
||||
:color="promptInfo.application.iconColor"
|
||||
>
|
||||
<v-img
|
||||
:src="promptInfo.application.iconUrl"
|
||||
width="80"
|
||||
height="80"
|
||||
aspect-ratio="1"
|
||||
contain
|
||||
>
|
||||
<template v-slot:placeholder>
|
||||
<div class="fill-height d-flex align-center justify-center">
|
||||
<v-icon :size="80">
|
||||
mdi-help
|
||||
</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-sheet>
|
||||
</v-sheet>
|
||||
</transition>
|
||||
</v-badge>
|
||||
</div>
|
||||
<v-window :value="step">
|
||||
<v-window-item :value="1">
|
||||
<overview-window
|
||||
:promptInfo="promptInfo"
|
||||
@next="toLoginWindow"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item :value="2" eager>
|
||||
<login-window
|
||||
ref="loginWindow"
|
||||
:prompt-info="promptInfo"
|
||||
@login="login"
|
||||
@back="loginBack"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item :value="3">
|
||||
<students-window
|
||||
v-if="students !== null"
|
||||
:prompt-info="promptInfo"
|
||||
:students="students"
|
||||
@back="toLoginWindow"
|
||||
/>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-sheet>
|
||||
</v-main>
|
||||
</v-app>
|
||||
<template v-slot:placeholder>
|
||||
<div class="fill-height d-flex align-center justify-center">
|
||||
<v-icon :size="80">
|
||||
mdi-help
|
||||
</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-sheet>
|
||||
</v-sheet>
|
||||
</transition>
|
||||
</v-badge>
|
||||
</div>
|
||||
<v-window :value="step">
|
||||
<v-window-item :value="1">
|
||||
<overview-window
|
||||
:promptInfo="promptInfo"
|
||||
@next="toLoginWindow"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item :value="2" eager>
|
||||
<login-window
|
||||
ref="loginWindow"
|
||||
:prompt-info="promptInfo"
|
||||
@login="login"
|
||||
@back="loginBack"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item :value="3">
|
||||
<students-window
|
||||
v-if="students !== null"
|
||||
:prompt-info="promptInfo"
|
||||
:students="students"
|
||||
@back="toLoginWindow"
|
||||
/>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card>
|
||||
</template>
|
||||
</dialog-app>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.authenticate-prompt-app {
|
||||
background-color: #f7f7f7 !important;
|
||||
|
||||
.avatar-sheet {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
@ -122,10 +111,6 @@
|
|||
.scale-enter, .scale-leave-to {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.v-card__text, .v-card__title {
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -136,10 +121,13 @@ import { PromptInfo, Student } from '@/types';
|
|||
import LoginWindow from '@/compontents/authenticate-prompt-windows/login-window.vue';
|
||||
import StudentsWindow from '@/compontents/authenticate-prompt-windows/students-window.vue';
|
||||
import { sdk } from '@/pages/authenticate-prompt/sdk';
|
||||
import DialogApp from '@/compontents/dialog-app.vue';
|
||||
|
||||
@Component({
|
||||
name: 'AuthenticatePromptApp',
|
||||
components: { LoginWindow, OverviewWindow, StudentsWindow },
|
||||
components: {
|
||||
LoginWindow, OverviewWindow, StudentsWindow, DialogApp,
|
||||
},
|
||||
})
|
||||
export default class AuthenticatePromptApp extends Vue {
|
||||
@Ref() readonly loginWindow!: LoginWindow
|
||||
|
|
48
website/src/pages/prompt-error/app.vue
Normal file
48
website/src/pages/prompt-error/app.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<dialog-app>
|
||||
<v-card outlined>
|
||||
<v-card-title>Podczas autoryzacji wystąpił błąd</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert text type="error">
|
||||
{{ errorMessage }}<br>
|
||||
<code
|
||||
v-if="errorDescription !== null"
|
||||
class="d-block mt-3 py-2 px-sm-3"
|
||||
>
|
||||
{{ errorDescription }}
|
||||
</code>
|
||||
</v-alert>
|
||||
Skontaktuj się z twórcą aplikacji w celu rozwiązania problemu
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</dialog-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import DialogApp from '@/compontents/dialog-app.vue';
|
||||
|
||||
@Component({
|
||||
name: 'PromptErrorApp',
|
||||
components: { DialogApp },
|
||||
})
|
||||
export default class PromptErrorApp extends Vue {
|
||||
errorCode: string | null = null;
|
||||
|
||||
errorDescription: string | null = null;
|
||||
|
||||
get errorMessage() {
|
||||
if (this.errorCode === 'invalid_request') return 'Niepoprawny format zapytania';
|
||||
if (this.errorCode === 'internal') return 'Błąd serwera';
|
||||
if (this.errorCode === 'unknown_application') return 'Nie znaleziono aplikacji';
|
||||
if (this.errorCode === 'unknown_redirect_uri') return 'Niepoprawne URI przekierowania';
|
||||
return 'Nieznany błąd';
|
||||
}
|
||||
|
||||
async created() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
this.errorCode = searchParams.get('code');
|
||||
this.errorDescription = searchParams.get('description');
|
||||
}
|
||||
}
|
||||
</script>
|
10
website/src/pages/prompt-error/main.ts
Normal file
10
website/src/pages/prompt-error/main.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Vue from 'vue';
|
||||
import vuetify from '@/plugins/vuetify';
|
||||
import AuthenticatePromptApp from './app.vue';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
vuetify,
|
||||
render: (h) => h(AuthenticatePromptApp),
|
||||
}).$mount('#app');
|
|
@ -7,7 +7,13 @@ module.exports = {
|
|||
entry: 'src/pages/authenticate-prompt/main.ts',
|
||||
template: 'public/index.html',
|
||||
filename: 'authenticate-prompt.html',
|
||||
title: 'Authorize application | Wulkanowy Bridge',
|
||||
title: 'Autoryzuj aplikację | Wulkanowy Bridge',
|
||||
},
|
||||
'prompt-error': {
|
||||
entry: 'src/pages/prompt-error/main.ts',
|
||||
template: 'public/index.html',
|
||||
filename: 'prompt-error.html',
|
||||
title: 'Błąd autoryzacji | Wulkanowy Bridge',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue