Skip to content

Commit 2875d6e

Browse files
committed
Token redirect page
1 parent b5df35c commit 2875d6e

3 files changed

Lines changed: 186 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<script lang="ts">
2+
import { apiTokensApi } from '$lib/api';
3+
import { PermissionType, instanceOfPermissionType } from '$lib/api/internal/v1';
4+
import LoadingCircle from '$lib/components/svg/LoadingCircle.svelte';
5+
import Button from '$lib/components/ui/button/button.svelte';
6+
import * as Card from '$lib/components/ui/card/index.js';
7+
import { onMount } from 'svelte';
8+
import { toast } from 'svelte-sonner';
9+
import type { QueryParamsType } from './queryParamsType';
10+
11+
let windowQueryParams = $state<
12+
| QueryParamsType
13+
| {
14+
error: string;
15+
}
16+
>({
17+
error: 'Loading...',
18+
});
19+
20+
let creatingToken = $state(false);
21+
let tokenSecret = $state<string | null>(null);
22+
23+
function getQueryParams():
24+
| QueryParamsType
25+
| {
26+
error: string;
27+
} {
28+
if (typeof window === 'undefined') {
29+
throw new Error('getQueryParams can only be called in the browser');
30+
}
31+
32+
const queryString = window.location.search;
33+
const params = new URLSearchParams(queryString);
34+
35+
const name = params.get('name');
36+
const redirectUri = params.get('redirect_uri');
37+
const permissions = params.get('permissions');
38+
39+
if (!name || !redirectUri || !permissions) {
40+
return {
41+
error:
42+
'Required get parameters are missing. Make sure name, redirect_uri, and permissions are provided.',
43+
};
44+
}
45+
46+
const permissionsArray = permissions.split(',');
47+
if (permissionsArray.length === 0) {
48+
return {
49+
error: 'Permissions cannot be empty',
50+
};
51+
}
52+
53+
let invalidPermissions: string[] = [];
54+
55+
permissionsArray.forEach((p) => {
56+
if (!instanceOfPermissionType(p)) {
57+
invalidPermissions.push(p);
58+
}
59+
});
60+
61+
if (invalidPermissions.length > 0) {
62+
return {
63+
error: `Invalid permissions provided: ${invalidPermissions.join(', ')}`,
64+
};
65+
}
66+
67+
return {
68+
name: name,
69+
redirectUri: redirectUri,
70+
permissions: permissionsArray.map((p) => p as PermissionType),
71+
};
72+
}
73+
74+
function allowRequest() {
75+
const params = windowQueryParams as QueryParamsType;
76+
77+
creatingToken = true;
78+
79+
apiTokensApi
80+
.tokensCreateToken({
81+
name: params.name,
82+
permissions: params.permissions as PermissionType[],
83+
})
84+
.then((response) => {
85+
tokenSecret = response.token;
86+
openUrl();
87+
})
88+
.catch((error) => {
89+
// Handle error in token creation, e.g., show an error message
90+
console.error('Error creating token:', error);
91+
toast.error('Failed to create token');
92+
});
93+
}
94+
95+
function openUrl() {
96+
if (!tokenSecret) {
97+
console.error('Token secret is not available');
98+
toast.error('Token secret is not available');
99+
return;
100+
}
101+
102+
if (!windowQueryParams) {
103+
console.error('Redirect URI is not available');
104+
toast.error('Redirect URI is not available');
105+
return;
106+
}
107+
108+
const redirectUriReplaced = (windowQueryParams as QueryParamsType).redirectUri.replace('%', tokenSecret);;
109+
110+
console.log('Redirecting to:', redirectUriReplaced);
111+
window.location.href = redirectUriReplaced;
112+
}
113+
114+
onMount(() => {
115+
windowQueryParams = getQueryParams();
116+
});
117+
</script>
118+
119+
<div class="flex items-center justify-center h-full w-full">
120+
<Card.Root class="w-lg max-w-2xl">
121+
{#if 'error' in windowQueryParams}
122+
<Card.Header>
123+
<Card.Title class="text-2xl">API Token Request</Card.Title>
124+
</Card.Header>
125+
<Card.Content>
126+
<span class="text-red-500">
127+
{windowQueryParams.error}
128+
</span>
129+
</Card.Content>
130+
{:else}
131+
<Card.Header>
132+
<Card.Title class="text-2xl">API Token Request</Card.Title>
133+
<Card.Description
134+
>An external application is requesting access to your account. Please review the
135+
permissions it is requesting before allowing access</Card.Description
136+
>
137+
</Card.Header>
138+
<Card.Content class="flex flex-col space-y-4">
139+
<span>
140+
<b>{windowQueryParams.name}</b> is requesting an API Token with the following permissions:
141+
</span>
142+
<ul class="list-disc pl-5">
143+
{#each windowQueryParams.permissions as permission}
144+
<li>{permission}</li>
145+
{/each}
146+
</ul>
147+
<span class="mt-4">
148+
Your token will be shared with the application at<br /><b
149+
>{windowQueryParams.redirectUri}</b
150+
></span
151+
>
152+
</Card.Content>
153+
<Card.Footer class="flex justify-end space-x-2">
154+
<Button class="m-w-16" onclick={allowRequest} disabled={creatingToken && !tokenSecret} color="yellow">
155+
{#if tokenSecret}
156+
<span>Open Again</span>
157+
{:else if creatingToken}
158+
<LoadingCircle />
159+
{:else}
160+
Allow
161+
{/if}
162+
</Button>
163+
<Button class="w-16" href="/" variant="outline">Deny</Button>
164+
</Card.Footer>
165+
{/if}
166+
</Card.Root>
167+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { PermissionType } from "$lib/api/internal/v1";
2+
3+
export type QueryParamsType = {
4+
name: string;
5+
redirectUri: string;
6+
permissions: PermissionType[];
7+
}

src/routes/t/+server.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { RequestHandler } from './$types';
2+
3+
export const GET: RequestHandler = ({ url }) => {
4+
const redirectUrl = url.origin + '/settings/api-tokens/new?' + url.searchParams.toString();
5+
return new Response(String('Redirecting...'), {
6+
status: 308,
7+
headers: {
8+
'Location': redirectUrl
9+
}
10+
});
11+
};
12+

0 commit comments

Comments
 (0)