added apple oauth2 integration

This commit is contained in:
Gani Georgiev
2023-03-01 23:29:45 +02:00
parent 41f01bab0d
commit f5e5fae773
68 changed files with 1019 additions and 242 deletions
@@ -48,9 +48,9 @@
<i class={icon} />
{/if}
<span class="txt">{title}</span>
<code title="Provider key">
{key.substring(0, key.length - 4)}
</code>
<em class="txt-hint">
({key.substring(0, key.length - 4)})
</em>
</div>
<div class="flex-fill" />
@@ -96,7 +96,7 @@
name="{key}.clientSecret"
let:uniqueId
>
<label for={uniqueId}>Client Secret</label>
<label for={uniqueId}>Client secret</label>
<RedactedPasswordInput
bind:value={config.clientSecret}
id={uniqueId}
@@ -117,14 +117,15 @@
</form>
<svelte:fragment slot="footer">
<button type="button" class="btn btn-transparent" on:click={hide} disabled={isSubmitting}>Close</button>
<button type="button" class="btn btn-transparent" on:click={hide} disabled={isSubmitting}
>Close</button
>
<button
type="submit"
form={formId}
class="btn btn-expanded"
class:btn-loading={isSubmitting}
disabled={!canSubmit || isSubmitting}
on:click={() => submit()}
>
<i class="ri-mail-send-line" />
<span class="txt">Send</span>
@@ -22,6 +22,8 @@
$: hasChanges = initialHash != JSON.stringify(formSettings);
$: totalHidden = Object.values(providersList).filter((provider) => provider.hidden).length;
loadSettings();
async function loadSettings() {
@@ -115,7 +117,7 @@
on:click={() => (showHidden = true)}
>
<i class="ri-arrow-down-s-line" />
<span class="txt">Show all</span>
<span class="txt">Show all ({totalHidden})</span>
</button>
{/if}
@@ -0,0 +1,24 @@
<script>
import AppleSecretPopup from "@/components/settings/providers/AppleSecretPopup.svelte";
export let key = "";
export let config = {};
let generatorPopup;
</script>
<button
type="button"
class="btn btn-sm btn-secondary btn-provider-{key}"
on:click={() => generatorPopup?.show({ clientId: config.clientId })}
>
<i class="ri-key-line" />
<span class="txt">Generate secret</span>
</button>
<AppleSecretPopup
bind:this={generatorPopup}
on:submit={(e) => {
config.clientSecret = e.detail?.secret || "";
}}
/>
@@ -0,0 +1,156 @@
<script>
import { createEventDispatcher } from "svelte";
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { addSuccessToast } from "@/stores/toasts";
import { setErrors } from "@/stores/errors";
import tooltip from "@/actions/tooltip";
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
import Field from "@/components/base/Field.svelte";
const dispatch = createEventDispatcher();
const formId = "apple_secret_" + CommonHelper.randomString(5);
const maxDuration = 15777000; // 6 months
let panel;
let clientId;
let teamId;
let keyId;
let privateKey;
let duration;
let isSubmitting = false;
$: canSubmit = true;
export function show(config = {}) {
clientId = config.clientId || "";
teamId = config.teamId || "";
keyId = config.keyId || "";
privateKey = config.privateKey || "";
duration = config.duration || maxDuration;
setErrors({}); // reset any previous errors
panel?.show();
}
export function hide() {
return panel?.hide();
}
async function submit() {
isSubmitting = true;
try {
// @todo replace with dedicated SDK method
const result = await ApiClient.send("/api/settings/apple/generate-client-secret", {
method: "POST",
body: {
teamId: teamId,
clientId: clientId,
keyId: keyId,
privateKey: privateKey.trim(),
duration: duration,
},
});
isSubmitting = false;
addSuccessToast("Successfully generated client secret.");
dispatch("submit", result);
panel?.hide();
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isSubmitting = false;
}
</script>
<OverlayPanel
bind:this={panel}
overlayClose={!isSubmitting}
escClose={!isSubmitting}
beforeHide={() => !isSubmitting}
popup
on:show
on:hide
>
<svelte:fragment slot="header">
<h4 class="center txt-break">Generate Apple client secret</h4>
</svelte:fragment>
<form id={formId} autocomplete="off" on:submit|preventDefault={() => submit()}>
<div class="grid">
<div class="col-lg-6">
<Field class="form-field required" name="clientId" let:uniqueId>
<label for={uniqueId}>Client ID</label>
<input type="text" id={uniqueId} bind:value={clientId} required />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="teamId" let:uniqueId>
<label for={uniqueId}>Team ID</label>
<input type="text" id={uniqueId} bind:value={teamId} required />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="keyId" let:uniqueId>
<label for={uniqueId}>Key ID</label>
<input type="text" id={uniqueId} bind:value={keyId} required />
</Field>
</div>
<div class="col-lg-6">
<Field class="form-field required" name="duration" let:uniqueId>
<label for={uniqueId}>
<span class="txt">Duration (in seconds)</span>
<i
class="ri-information-line link-hint"
use:tooltip={{
text: `Max ${maxDuration} seconds (~${
(maxDuration / (60 * 60 * 24 * 30)) << 0
} months).`,
position: "top",
}}
/>
</label>
<input type="text" id={uniqueId} max={maxDuration} bind:value={duration} required />
</Field>
</div>
<Field class="form-field required" name="privateKey" let:uniqueId>
<label for={uniqueId}>Private key</label>
<textarea
id={uniqueId}
required
rows="8"
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
bind:value={privateKey}
/>
<div class="help-block">
The key is not stored on the server and it is used only for generating the signed JWT.
</div>
</Field>
</div>
</form>
<svelte:fragment slot="footer">
<button type="button" class="btn btn-transparent" on:click={hide} disabled={isSubmitting}
>Close</button
>
<button
type="submit"
form={formId}
class="btn btn-expanded"
class:btn-loading={isSubmitting}
disabled={!canSubmit || isSubmitting}
>
<i class="ri-key-line" />
<span class="txt">Generate and set secret</span>
</button>
</svelte:fragment>
</OverlayPanel>
@@ -19,7 +19,7 @@
<div class="col-lg-12">
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.tokenUrl" let:uniqueId>
<label for={uniqueId}>Token URL</label>
<input type="text" id={uniqueId} bind:value={config.tokenUrl} required={config.enabled} />
<input type="url" id={uniqueId} bind:value={config.tokenUrl} required={config.enabled} />
<div class="help-block">
Eg. {`https://login.microsoftonline.com/YOUR_DIRECTORY_TENANT_ID/oauth2/v2.0/token`}
</div>
@@ -17,14 +17,14 @@
<div class="col-lg-12">
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.tokenUrl" let:uniqueId>
<label for={uniqueId}>Token URL</label>
<input type="text" id={uniqueId} bind:value={config.tokenUrl} required={config.enabled} />
<input type="url" id={uniqueId} bind:value={config.tokenUrl} required={config.enabled} />
<div class="help-block">Eg. https://example.com/token/</div>
</Field>
</div>
<div class="col-lg-12">
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.userApiUrl" let:uniqueId>
<label for={uniqueId}>User API URL</label>
<input type="text" id={uniqueId} bind:value={config.userApiUrl} required={config.enabled} />
<input type="url" id={uniqueId} bind:value={config.userApiUrl} required={config.enabled} />
<div class="help-block">Eg. https://example.com/userinfo/</div>
</Field>
</div>
@@ -16,13 +16,13 @@
<div class="col-lg-4">
<Field class="form-field" name="{key}.tokenUrl" let:uniqueId>
<label for={uniqueId}>Token URL</label>
<input type="text" id={uniqueId} bind:value={config.tokenUrl} />
<input type="url" id={uniqueId} bind:value={config.tokenUrl} />
</Field>
</div>
<div class="col-lg-4">
<Field class="form-field" name="{key}.userApiUrl" let:uniqueId>
<label for={uniqueId}>User API URL</label>
<input type="text" id={uniqueId} bind:value={config.userApiUrl} />
<input type="url" id={uniqueId} bind:value={config.userApiUrl} />
</Field>
</div>
</div>
+35 -29
View File
@@ -1,6 +1,7 @@
import SelfHostedOptions from "@/components/settings/providers/SelfHostedOptions.svelte";
import MicrosoftOptions from "@/components/settings/providers/MicrosoftOptions.svelte";
import OIDCOptions from "@/components/settings/providers/OIDCOptions.svelte";
import OIDCOptions from "@/components/settings/providers/OIDCOptions.svelte";
import AppleOptions from "@/components/settings/providers/AppleOptions.svelte";
// Object list with all supported OAuth2 providers in the format:
// ```
@@ -11,10 +12,20 @@ import OIDCOptions from "@/components/settings/providers/OIDCOptions.svelte";
// - `key` - the provider settings key (eg. "gitlabAuth")
// - `config` - the provider settings config that is currently being updated
export default {
appleAuth: {
title: "Apple",
icon: "ri-apple-fill",
optionsComponent: AppleOptions,
},
googleAuth: {
title: "Google",
icon: "ri-google-fill",
},
microsoftAuth: {
title: "Microsoft",
icon: "ri-microsoft-fill",
optionsComponent: MicrosoftOptions,
},
facebookAuth: {
title: "Facebook",
icon: "ri-facebook-fill",
@@ -32,31 +43,6 @@ export default {
icon: "ri-gitlab-fill",
optionsComponent: SelfHostedOptions,
},
discordAuth: {
title: "Discord",
icon: "ri-discord-fill",
},
microsoftAuth: {
title: "Microsoft",
icon: "ri-microsoft-fill",
optionsComponent: MicrosoftOptions,
},
spotifyAuth: {
title: "Spotify",
icon: "ri-spotify-fill",
},
kakaoAuth: {
title: "Kakao",
icon: "ri-kakao-talk-fill",
},
twitchAuth: {
title: "Twitch",
icon: "ri-twitch-fill",
},
stravaAuth: {
title: "Strava",
icon: "ri-riding-fill",
},
giteeAuth: {
title: "Gitee",
icon: "ri-git-repository-fill",
@@ -66,23 +52,43 @@ export default {
icon: "ri-cup-fill",
optionsComponent: SelfHostedOptions,
},
discordAuth: {
title: "Discord",
icon: "ri-discord-fill",
},
kakaoAuth: {
title: "Kakao",
icon: "ri-kakao-talk-fill",
},
spotifyAuth: {
title: "Spotify",
icon: "ri-spotify-fill",
},
twitchAuth: {
title: "Twitch",
icon: "ri-twitch-fill",
},
stravaAuth: {
title: "Strava",
icon: "ri-riding-fill",
},
livechatAuth: {
title: "LiveChat",
icon: "ri-chat-1-fill",
},
oidcAuth: {
title: "OpenID Connect (Authentik, Keycloak, Okta, ...)",
title: "OpenID Connect - Authentik, Keycloak, Okta, etc.",
icon: "ri-lock-fill",
optionsComponent: OIDCOptions,
},
oidc2Auth: {
title: "(2) OpenID Connect (Authentik, Keycloak, Okta, ...)",
title: "(2) OpenID Connect - Authentik, Keycloak, Okta, etc.",
icon: "ri-lock-fill",
hidden: true,
optionsComponent: OIDCOptions,
},
oidc3Auth: {
title: "(3) OpenID Connect (Authentik, Keycloak, Okta, ...)",
title: "(3) OpenID Connect - Authentik, Keycloak, Okta, etc.",
icon: "ri-lock-fill",
hidden: true,
optionsComponent: OIDCOptions,