Add error screen

This commit is contained in:
Dominik Korsa 2021-01-22 17:47:26 +01:00
parent 800e545ff5
commit 3c1f07f3b0
No known key found for this signature in database
GPG key ID: 546F986F71A6FE6E
7 changed files with 217 additions and 107 deletions

View file

@ -18,17 +18,28 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
) => { ) => {
if (!isObject(request.query)) { if (!isObject(request.query)) {
server.log.warn('Request query is not an object'); 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 { try {
validateParam('client_id', request.query.client_id); validateParam('client_id', request.query.client_id);
validateParam('redirect_uri', request.query.redirect_uri); validateParam('redirect_uri', request.query.redirect_uri);
} catch (error) { } catch (error) {
if (error instanceof ParamError) { 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); server.log.error(error);
throw server.httpErrors.internalServerError(); await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=internal'));
return;
} }
const application = await database.applicationRepo.findOne({ const application = await database.applicationRepo.findOne({
@ -36,8 +47,14 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
clientId: request.query.client_id, clientId: request.query.client_id,
}, },
}); });
if (application === undefined) throw server.httpErrors.badRequest('Unknown application'); if (application === undefined) {
if (!application.redirectUris.includes(request.query.redirect_uri)) throw server.httpErrors.badRequest('Redirect URI not registered'); 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 { try {
validateParam('response_type', request.query.response_type); validateParam('response_type', request.query.response_type);

View file

@ -23,5 +23,11 @@ module.exports = {
'max-len': ['off'] 'max-len': ['off']
}, },
}, },
{
files: '**/*.vue',
rules: {
'class-methods-use-this': ['off']
}
}
], ],
}; };

View 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>

View file

@ -1,12 +1,5 @@
<template> <template>
<v-app class="authenticate-prompt-app"> <dialog-app class="authenticate-prompt-app">
<div>
<div class="text-h4 text-center mt-8">
<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">
<v-alert type="error" text v-if="!promptId"> <v-alert type="error" text v-if="!promptId">
Brak wymaganego parametru <code>prompt_id</code> Brak wymaganego parametru <code>prompt_id</code>
</v-alert> </v-alert>
@ -93,15 +86,11 @@
</v-window> </v-window>
</v-card> </v-card>
</template> </template>
</v-sheet> </dialog-app>
</v-main>
</v-app>
</template> </template>
<style lang="scss"> <style lang="scss">
.authenticate-prompt-app { .authenticate-prompt-app {
background-color: #f7f7f7 !important;
.avatar-sheet { .avatar-sheet {
border-radius: 50%; border-radius: 50%;
} }
@ -122,10 +111,6 @@
.scale-enter, .scale-leave-to { .scale-enter, .scale-leave-to {
transform: scale(0); transform: scale(0);
} }
.v-card__text, .v-card__title {
word-break: normal;
}
} }
</style> </style>
@ -136,10 +121,13 @@ import { PromptInfo, Student } from '@/types';
import LoginWindow from '@/compontents/authenticate-prompt-windows/login-window.vue'; import LoginWindow from '@/compontents/authenticate-prompt-windows/login-window.vue';
import StudentsWindow from '@/compontents/authenticate-prompt-windows/students-window.vue'; import StudentsWindow from '@/compontents/authenticate-prompt-windows/students-window.vue';
import { sdk } from '@/pages/authenticate-prompt/sdk'; import { sdk } from '@/pages/authenticate-prompt/sdk';
import DialogApp from '@/compontents/dialog-app.vue';
@Component({ @Component({
name: 'AuthenticatePromptApp', name: 'AuthenticatePromptApp',
components: { LoginWindow, OverviewWindow, StudentsWindow }, components: {
LoginWindow, OverviewWindow, StudentsWindow, DialogApp,
},
}) })
export default class AuthenticatePromptApp extends Vue { export default class AuthenticatePromptApp extends Vue {
@Ref() readonly loginWindow!: LoginWindow @Ref() readonly loginWindow!: LoginWindow

View 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>

View 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');

View file

@ -7,7 +7,13 @@ module.exports = {
entry: 'src/pages/authenticate-prompt/main.ts', entry: 'src/pages/authenticate-prompt/main.ts',
template: 'public/index.html', template: 'public/index.html',
filename: 'authenticate-prompt.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',
}, },
}, },
}; };