Skip to content

Commit 4e78562

Browse files
authored
OAuth support WIP (#141)
1 parent 4f50a61 commit 4e78562

5 files changed

Lines changed: 235 additions & 11 deletions

File tree

src/lib/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ export const shockersV2Api = new ShockersV2Api(DefaultApiV2Configuration);
5959
export const publicShockerSharesApi = new PublicShockerSharesApi(DefaultApiV1Configuration);
6060
export const shockerSharesV1Api = new ShockerSharesV1Api(DefaultApiV1Configuration);
6161
export const shockerSharesV2Api = new ShockerSharesV2Api(DefaultApiV2Configuration);
62-
export const usersApi = new UsersApi(DefaultApiV1Configuration);
6362
export const oauthApi = new OAuthApi(DefaultApiV1Configuration);
63+
export const usersApi = new UsersApi(DefaultApiV1Configuration);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
/**
4+
* OpenShock.API
5+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
6+
*
7+
* The version of the OpenAPI document: 1.0
8+
*
9+
*
10+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
11+
* https://openapi-generator.tech
12+
* Do not edit the class manually.
13+
*/
14+
15+
import { mapValues } from '../runtime';
16+
/**
17+
*
18+
* @export
19+
* @interface OAuthFinalizeResponse
20+
*/
21+
export interface OAuthFinalizeResponse {
22+
/**
23+
* The provider key that was processed.
24+
* @type {string}
25+
* @memberof OAuthFinalizeResponse
26+
*/
27+
provider: string;
28+
/**
29+
* The external account id that was linked.
30+
* @type {string}
31+
* @memberof OAuthFinalizeResponse
32+
*/
33+
externalId: string;
34+
/**
35+
* When action=create, the username of the newly created account.
36+
* @type {string}
37+
* @memberof OAuthFinalizeResponse
38+
*/
39+
username: string | null;
40+
}
41+
42+
/**
43+
* Check if a given object implements the OAuthFinalizeResponse interface.
44+
*/
45+
export function instanceOfOAuthFinalizeResponse(value: object): value is OAuthFinalizeResponse {
46+
if (!('provider' in value) || value['provider'] === undefined) return false;
47+
if (!('externalId' in value) || value['externalId'] === undefined) return false;
48+
if (!('username' in value) || value['username'] === undefined) return false;
49+
return true;
50+
}
51+
52+
export function OAuthFinalizeResponseFromJSON(json: any): OAuthFinalizeResponse {
53+
return OAuthFinalizeResponseFromJSONTyped(json, false);
54+
}
55+
56+
export function OAuthFinalizeResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): OAuthFinalizeResponse {
57+
if (json == null) {
58+
return json;
59+
}
60+
return {
61+
62+
'provider': json['provider'],
63+
'externalId': json['externalId'],
64+
'username': json['username'],
65+
};
66+
}
67+
68+
export function OAuthFinalizeResponseToJSON(json: any): OAuthFinalizeResponse {
69+
return OAuthFinalizeResponseToJSONTyped(json, false);
70+
}
71+
72+
export function OAuthFinalizeResponseToJSONTyped(value?: OAuthFinalizeResponse | null, ignoreDiscriminator: boolean = false): any {
73+
if (value == null) {
74+
return value;
75+
}
76+
77+
return {
78+
79+
'provider': value['provider'],
80+
'externalId': value['externalId'],
81+
'username': value['username'],
82+
};
83+
}
84+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
/**
4+
* OpenShock.API
5+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
6+
*
7+
* The version of the OpenAPI document: 1.0
8+
*
9+
*
10+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
11+
* https://openapi-generator.tech
12+
* Do not edit the class manually.
13+
*/
14+
15+
/**
16+
*
17+
* @export
18+
* @enum {string}
19+
*/
20+
export enum OAuthFlow {
21+
LoginOrCreate = 'LoginOrCreate',
22+
Link = 'Link'
23+
}
24+
25+
26+
export function instanceOfOAuthFlow(value: any): boolean {
27+
for (const key in OAuthFlow) {
28+
if (Object.prototype.hasOwnProperty.call(OAuthFlow, key)) {
29+
if (OAuthFlow[key as keyof typeof OAuthFlow] === value) {
30+
return true;
31+
}
32+
}
33+
}
34+
return false;
35+
}
36+
37+
export function OAuthFlowFromJSON(json: any): OAuthFlow {
38+
return OAuthFlowFromJSONTyped(json, false);
39+
}
40+
41+
export function OAuthFlowFromJSONTyped(json: any, ignoreDiscriminator: boolean): OAuthFlow {
42+
return json as OAuthFlow;
43+
}
44+
45+
export function OAuthFlowToJSON(value?: OAuthFlow | null): any {
46+
return value as any;
47+
}
48+
49+
export function OAuthFlowToJSONTyped(value: any, ignoreDiscriminator: boolean): OAuthFlow {
50+
return value as OAuthFlow;
51+
}
52+

src/routes/(anonymous)/login/+page.svelte

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script lang="ts">
22
import { goto } from '$app/navigation';
33
import { page } from '$app/state';
4+
import { PUBLIC_BACKEND_API_DOMAIN } from '$env/static/public';
45
import { accountV2Api } from '$lib/api';
56
import Container from '$lib/components/Container.svelte';
67
import Turnstile from '$lib/components/Turnstile.svelte';
@@ -52,6 +53,13 @@
5253
}
5354
}
5455
56+
async function startOAuth() {
57+
const provider = "discord";
58+
59+
// Set the current URL as the redirect URL so we can come back here after OAuth
60+
window.location.href = `https://${PUBLIC_BACKEND_API_DOMAIN}/1/oauth/${provider}/authorize`;
61+
}
62+
5563
let canSubmit = $derived(
5664
usernameOrEmail.length > 0 && password.length > 0 && turnstileResponse != null
5765
);
@@ -82,5 +90,7 @@
8290
<Button type="submit" disabled={!canSubmit}>Log In</Button>
8391

8492
<a class=" text-sm opacity-75 hover:underline" href="/forgot-password">Forgot your password?</a>
93+
94+
<Button type="button" onclick={startOAuth}>Log In With Discord</Button>
8595
</form>
8696
</Container>
Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,100 @@
11
<script lang="ts">
2+
import { goto } from '$app/navigation';
23
import { page } from '$app/state';
34
import { oauthApi } from '$lib/api';
5+
import Container from '$lib/components/Container.svelte';
6+
import EmailInput from '$lib/components/input/EmailInput.svelte';
7+
import UsernameInput from '$lib/components/input/UsernameInput.svelte';
8+
import { Button } from '$lib/components/ui/button';
9+
import { isValidationError, mapToValRes } from '$lib/errorhandling/ValidationProblemDetails';
10+
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
11+
import { UserStore } from '$lib/stores/UserStore';
412
import { onMount } from 'svelte';
513
6-
let provider = $derived(page.params.provider);
14+
let username = $state<string>('');
15+
let usernameValid = $state<boolean>(false);
716
8-
let displayName = $state('');
917
let email = $state('');
10-
let expires = $state<Date | null>(null);
18+
let emailValid = $state(false);
19+
20+
let password = $state('');
21+
22+
let canSubmit = $derived(
23+
usernameValid && emailValid
24+
);
25+
26+
async function handleSubmission(e: SubmitEvent) {
27+
e.preventDefault();
28+
29+
if (!username || !email) {
30+
return;
31+
}
32+
33+
try {
34+
const account = await oauthApi.oAuthOAuthSignupFinalize(page.params.provider!, {
35+
username,
36+
email,
37+
password,
38+
});
39+
40+
if (!account.isVerified) {
41+
goto('/login?message=signup-success');
42+
return;
43+
}
44+
45+
UserStore.setSelf({
46+
id: account.accountId,
47+
name: account.accountName,
48+
avatar: account.profileImage,
49+
email: account.accountEmail,
50+
roles: account.accountRoles,
51+
});
52+
53+
goto('/home');
54+
} catch (error) {
55+
await handleApiError(error, (problem) => {
56+
if (!isValidationError(problem)) return false;
57+
58+
console.log(mapToValRes(problem, 'Username'));
59+
console.log(mapToValRes(problem, 'Password'));
60+
console.log(mapToValRes(problem, 'Email'));
61+
console.log(mapToValRes(problem, 'TurnstileResponse'));
62+
63+
return true;
64+
});
65+
}
66+
}
1167
1268
onMount(() => {
13-
if (!provider) return;
14-
oauthApi.oAuthOAuthSignupGetData(provider).then((resp) => {
15-
if (resp.displayName) displayName = resp.displayName;
16-
if (resp.email) email = resp.email;
17-
expires = resp.expiresAt;
18-
});
69+
oauthApi.oAuthOAuthSignupGetData(page.params.provider!)
70+
.then(data => {
71+
if (data.displayName) {
72+
username = data.displayName;
73+
usernameValid = true;
74+
}
75+
if (data.email) {
76+
email = data.email;
77+
emailValid = true;
78+
}
79+
})
80+
.catch(err => {
81+
console.error('Failed to fetch OAuth signup data', err);
82+
goto('/login');
83+
});
1984
});
2085
</script>
2186

22-
{page.params.provider}
87+
<Container class="items-center">
88+
<form class="flex flex-col gap-2" onsubmit={handleSubmission}>
89+
<div class="text-3xl font-semibold text-nowrap">Sign Up With <span class="capitalize">{page.params.provider}</span></div>
90+
<UsernameInput
91+
label="Username"
92+
placeholder="Username"
93+
bind:value={username}
94+
bind:valid={usernameValid}
95+
/>
96+
<EmailInput label="Email" placeholder="Email" bind:value={email} bind:valid={emailValid} />
97+
98+
<Button type="submit" disabled={!canSubmit}>Sign Up</Button>
99+
</form>
100+
</Container>

0 commit comments

Comments
 (0)