updated OAuth2 providers ui
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
code: 200,
|
||||
body: JSON.stringify(
|
||||
{
|
||||
token: "JWT_TOKEN",
|
||||
token: "JWT_AUTH_TOKEN",
|
||||
record: CommonHelper.dummyCollectionRecord(collection),
|
||||
meta: {
|
||||
id: "abc123",
|
||||
@@ -25,6 +25,9 @@
|
||||
username: "john.doe",
|
||||
email: "test@example.com",
|
||||
avatarUrl: "https://example.com/avatar.png",
|
||||
accessToken: "...",
|
||||
refreshToken: "...",
|
||||
rawUser: {},
|
||||
},
|
||||
},
|
||||
null,
|
||||
@@ -52,11 +55,10 @@
|
||||
<h3 class="m-b-sm">Auth with OAuth2 ({collection.name})</h3>
|
||||
<div class="content txt-lg m-b-sm">
|
||||
<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
|
||||
<p>This action usually should be called right after the provider login page redirect.</p>
|
||||
<p>
|
||||
You could also check the
|
||||
For more details please check the
|
||||
<a href={import.meta.env.PB_OAUTH2_EXAMPLE} target="_blank" rel="noopener noreferrer">
|
||||
OAuth2 web integration example
|
||||
OAuth2 integration documentation
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
@@ -69,49 +71,46 @@
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${collection?.name}').authWithOAuth2(
|
||||
'google',
|
||||
'CODE',
|
||||
'VERIFIER',
|
||||
'REDIRECT_URL',
|
||||
// optional data that will be used for the new account on OAuth2 sign-up
|
||||
{
|
||||
'name': 'test',
|
||||
},
|
||||
);
|
||||
// This method initializes a one-off realtime subscription and will
|
||||
// open a popup window with the OAuth2 vendor page to authenticate.
|
||||
//
|
||||
// Once the external OAuth2 sign-in/sign-up flow is completed, the popup
|
||||
// window will be automatically closed and the OAuth2 data sent back
|
||||
// to the user through the previously established realtime connection.
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`}
|
||||
dart={`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final pb = PocketBase('${backendAbsUrl}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${collection?.name}').authWithOAuth2(
|
||||
'google',
|
||||
'CODE',
|
||||
'VERIFIER',
|
||||
'REDIRECT_URL',
|
||||
// optional data that will be used for the new account on OAuth2 sign-up
|
||||
createData: {
|
||||
'name': 'test',
|
||||
},
|
||||
);
|
||||
// This method initializes a one-off realtime subscription and will
|
||||
// call the provided urlCallback with the OAuth2 vendor url to authenticate.
|
||||
//
|
||||
// Once the external OAuth2 sign-in/sign-up flow is completed, the browser
|
||||
// window will be automatically closed and the OAuth2 data sent back
|
||||
// to the user through the previously established realtime connection.
|
||||
final authData = await pb.collection('users').authWithOAuth2('google', (url) async {
|
||||
await launchUrl(url);
|
||||
});
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`}
|
||||
/>
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<script>
|
||||
import { scale, slide } from "svelte/transition";
|
||||
import tooltip from "@/actions/tooltip";
|
||||
import { errors, removeError } from "@/stores/errors";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import Accordion from "@/components/base/Accordion.svelte";
|
||||
import Field from "@/components/base/Field.svelte";
|
||||
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
|
||||
|
||||
export let key;
|
||||
export let title;
|
||||
export let icon = "";
|
||||
export let config = {};
|
||||
export let optionsComponent;
|
||||
|
||||
let accordion;
|
||||
|
||||
$: hasErrors = !CommonHelper.isEmpty(CommonHelper.getNestedVal($errors, key));
|
||||
|
||||
$: if (!config.enabled) {
|
||||
removeError(key);
|
||||
}
|
||||
|
||||
export function expand() {
|
||||
accordion?.expand();
|
||||
}
|
||||
|
||||
export function collapse() {
|
||||
accordion?.collapse();
|
||||
}
|
||||
|
||||
export function collapseSiblings() {
|
||||
accordion?.collapseSiblings();
|
||||
}
|
||||
|
||||
function clear() {
|
||||
for (let k in config) {
|
||||
config[k] = "";
|
||||
}
|
||||
config.enabled = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Accordion bind:this={accordion} on:expand on:collapse on:toggle {...$$restProps}>
|
||||
<svelte:fragment slot="header">
|
||||
<div class="inline-flex">
|
||||
{#if icon}
|
||||
<i class={icon} class:txt-hint={!config.enabled} />
|
||||
{/if}
|
||||
<span class="txt">{title}</span>
|
||||
<em class="txt-hint">
|
||||
({key.substring(0, key.length - 4)})
|
||||
</em>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill" />
|
||||
|
||||
{#if hasErrors}
|
||||
<i
|
||||
class="ri-error-warning-fill txt-danger"
|
||||
transition:scale|local={{ duration: 150, start: 0.7 }}
|
||||
use:tooltip={{ text: "Has errors", position: "left" }}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if config.enabled}
|
||||
<span class="label label-success">Enabled</span>
|
||||
{:else}
|
||||
<span class="label label-hint">Disabled</span>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex">
|
||||
<Field class="form-field form-field-toggle m-b-0" name="{key}.enabled" let:uniqueId>
|
||||
<input type="checkbox" id={uniqueId} bind:checked={config.enabled} />
|
||||
<label for={uniqueId}>Enable</label>
|
||||
</Field>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-transparent btn-hint m-l-auto" on:click={clear}>
|
||||
<span class="txt">Clear all fields</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 spacing" />
|
||||
<div class="col-lg-6">
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.clientId" let:uniqueId>
|
||||
<label for={uniqueId}>Client ID</label>
|
||||
<input type="text" id={uniqueId} bind:value={config.clientId} required={config.enabled} />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<Field
|
||||
class="form-field {config.enabled ? 'required' : ''}"
|
||||
name="{key}.clientSecret"
|
||||
let:uniqueId
|
||||
>
|
||||
<label for={uniqueId}>Client secret</label>
|
||||
<RedactedPasswordInput
|
||||
bind:value={config.clientSecret}
|
||||
id={uniqueId}
|
||||
required={config.enabled}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
{#if optionsComponent}
|
||||
<div class="col-lg-12">
|
||||
<svelte:component this={optionsComponent} {key} bind:config />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Accordion>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script>
|
||||
import AuthProviderPanel from "@/components/settings/AuthProviderPanel.svelte";
|
||||
|
||||
export let provider = {};
|
||||
export let config = {};
|
||||
|
||||
let providerPanel;
|
||||
</script>
|
||||
|
||||
<div class="provider-card">
|
||||
<figure class="provider-logo">
|
||||
{#if provider.logo}
|
||||
<img src={provider.logo} alt="{provider.title} logo" />
|
||||
{/if}
|
||||
</figure>
|
||||
<div class="title">{provider.title}</div>
|
||||
<em class="txt-hint txt-sm m-r-auto">({provider.key.slice(0, -4)})</em>
|
||||
{#if config.enabled}
|
||||
<div class="label label-success">Enabled</div>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-circle btn-hint btn-transparent"
|
||||
aria-label="Provider settings"
|
||||
on:click={() => {
|
||||
providerPanel?.show(
|
||||
provider,
|
||||
Object.assign({}, config, {
|
||||
enabled: config.clientId ? config.enabled : true,
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
<i class="ri-settings-4-line" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<AuthProviderPanel
|
||||
bind:this={providerPanel}
|
||||
on:submit={(e) => {
|
||||
if (e.detail[provider.key]) {
|
||||
config = e.detail[provider.key];
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
.provider-logo {
|
||||
$boxSize: 32px;
|
||||
$imgSize: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: $boxSize;
|
||||
height: $boxSize;
|
||||
border-radius: var(--baseRadius);
|
||||
background: var(--bodyColor);
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
img {
|
||||
max-width: $imgSize;
|
||||
max-height: $imgSize;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.provider-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
border-radius: var(--baseRadius);
|
||||
border: 1px solid var(--baseAlt1Color);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,124 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import { setErrors } from "@/stores/errors";
|
||||
import { addSuccessToast } from "@/stores/toasts";
|
||||
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
||||
import Field from "@/components/base/Field.svelte";
|
||||
import RedactedPasswordInput from "@/components/base/RedactedPasswordInput.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const formId = "provider_popup_" + CommonHelper.randomString(5);
|
||||
|
||||
let panel;
|
||||
let provider = {};
|
||||
let config = {};
|
||||
let isSubmitting = false;
|
||||
let initialHash = "";
|
||||
|
||||
$: hasChanges = JSON.stringify(config) != initialHash;
|
||||
|
||||
export function show(showProvider, showConfig) {
|
||||
setErrors({}); // reset any previous errors
|
||||
|
||||
provider = Object.assign({}, showProvider);
|
||||
config = Object.assign({ enabled: true }, showConfig);
|
||||
initialHash = JSON.stringify(config);
|
||||
|
||||
panel?.show();
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
return panel?.hide();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
isSubmitting = true;
|
||||
|
||||
try {
|
||||
const data = {};
|
||||
data[provider.key] = CommonHelper.filterRedactedProps(config);
|
||||
|
||||
const result = await ApiClient.settings.update(data);
|
||||
|
||||
setErrors({});
|
||||
|
||||
addSuccessToast("Successfully updated provider settings.");
|
||||
|
||||
dispatch("submit", result);
|
||||
|
||||
hide();
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
}
|
||||
|
||||
isSubmitting = false;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
for (let k in config) {
|
||||
config[k] = "";
|
||||
}
|
||||
config.enabled = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<OverlayPanel bind:this={panel} overlayClose={!isSubmitting} escClose={!isSubmitting} on:show on:hide>
|
||||
<svelte:fragment slot="header">
|
||||
<h4 class="center txt-break">{provider.title || provider.key} provider</h4>
|
||||
</svelte:fragment>
|
||||
|
||||
<form id={formId} autocomplete="off" on:submit|preventDefault={() => submit()}>
|
||||
<div class="flex m-b-base">
|
||||
<Field class="form-field form-field-toggle m-b-0" name="{provider.key}.enabled" let:uniqueId>
|
||||
<input type="checkbox" id={uniqueId} bind:checked={config.enabled} />
|
||||
<label for={uniqueId}>Enable</label>
|
||||
</Field>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-transparent btn-hint m-l-auto" on:click={clear}>
|
||||
<span class="txt">Clear all fields</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Field
|
||||
class="form-field {config.enabled ? 'required' : ''}"
|
||||
name="{provider.key}.clientId"
|
||||
let:uniqueId
|
||||
>
|
||||
<label for={uniqueId}>Client ID</label>
|
||||
<input type="text" id={uniqueId} bind:value={config.clientId} required={config.enabled} />
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
class="form-field {config.enabled ? 'required' : ''}"
|
||||
name="{provider.key}.clientSecret"
|
||||
let:uniqueId
|
||||
>
|
||||
<label for={uniqueId}>Client secret</label>
|
||||
<RedactedPasswordInput bind:value={config.clientSecret} id={uniqueId} required={config.enabled} />
|
||||
</Field>
|
||||
|
||||
{#if provider.optionsComponent}
|
||||
<div class="col-lg-12">
|
||||
<svelte:component this={provider.optionsComponent} key={provider.key} bind:config />
|
||||
</div>
|
||||
{/if}
|
||||
</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={!hasChanges || isSubmitting}
|
||||
>
|
||||
<span class="txt">Save changes</span>
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
</OverlayPanel>
|
||||
@@ -1,28 +1,19 @@
|
||||
<script>
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import { pageTitle } from "@/stores/app";
|
||||
import { setErrors } from "@/stores/errors";
|
||||
import { addSuccessToast } from "@/stores/toasts";
|
||||
import PageWrapper from "@/components/base/PageWrapper.svelte";
|
||||
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
|
||||
import AuthProviderAccordion from "@/components/settings/AuthProviderAccordion.svelte";
|
||||
import AuthProviderCard from "@/components/settings/AuthProviderCard.svelte";
|
||||
import providersList from "@/providers.js";
|
||||
|
||||
$pageTitle = "Auth providers";
|
||||
|
||||
let accordions = {};
|
||||
let originalFormSettings = {};
|
||||
let formSettings = {};
|
||||
let isLoading = false;
|
||||
let isSaving = false;
|
||||
let showHidden = false;
|
||||
let formSettings = {};
|
||||
|
||||
$: initialHash = JSON.stringify(originalFormSettings);
|
||||
$: enabledProviders = providersList.filter((provider) => formSettings[provider.key]?.enabled);
|
||||
|
||||
$: hasChanges = initialHash != JSON.stringify(formSettings);
|
||||
|
||||
$: totalHidden = Object.values(providersList).filter((provider) => provider.hidden).length;
|
||||
$: disabledProviders = providersList.filter((provider) => !formSettings[provider.key]?.enabled);
|
||||
|
||||
loadSettings();
|
||||
|
||||
@@ -39,41 +30,13 @@
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (isSaving || !hasChanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSaving = true;
|
||||
|
||||
try {
|
||||
const result = await ApiClient.settings.update(CommonHelper.filterRedactedProps(formSettings));
|
||||
initSettings(result);
|
||||
setErrors({});
|
||||
|
||||
accordions[Object.keys(accordions)[0]]?.collapseSiblings();
|
||||
addSuccessToast("Successfully updated auth providers.");
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
}
|
||||
|
||||
isSaving = false;
|
||||
}
|
||||
|
||||
function initSettings(data) {
|
||||
data = data || {};
|
||||
|
||||
formSettings = {};
|
||||
|
||||
for (const providerKey in providersList) {
|
||||
formSettings[providerKey] = Object.assign({ enabled: false }, data[providerKey]);
|
||||
for (const provider of providersList) {
|
||||
formSettings[provider.key] = Object.assign({ enabled: false }, data[provider.key]);
|
||||
}
|
||||
|
||||
originalFormSettings = JSON.parse(JSON.stringify(formSettings));
|
||||
}
|
||||
|
||||
function reset() {
|
||||
formSettings = JSON.parse(JSON.stringify(originalFormSettings || {}));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -88,61 +51,32 @@
|
||||
</header>
|
||||
|
||||
<div class="wrapper">
|
||||
<form class="panel" autocomplete="off" on:submit|preventDefault={save}>
|
||||
<div class="panel">
|
||||
<h6 class="m-b-base">Manage the allowed users OAuth2 sign-in/sign-up methods.</h6>
|
||||
|
||||
{#if isLoading}
|
||||
<div class="loader" />
|
||||
{:else}
|
||||
<div class="accordions">
|
||||
{#each Object.entries(providersList) as [key, provider]}
|
||||
{#if showHidden || !provider.hidden || formSettings[key]?.enabled}
|
||||
<AuthProviderAccordion
|
||||
bind:this={accordions[key]}
|
||||
single
|
||||
{key}
|
||||
title={provider.title}
|
||||
icon={provider.icon || "ri-fingerprint-line"}
|
||||
optionsComponent={provider.optionsComponent}
|
||||
bind:config={formSettings[key]}
|
||||
/>
|
||||
{/if}
|
||||
<div class="grid grid-sm">
|
||||
{#each enabledProviders as provider (provider.key)}
|
||||
<div class="col-lg-6">
|
||||
<AuthProviderCard {provider} bind:config={formSettings[provider.key]} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if !showHidden}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-transparent btn-hint m-t-10"
|
||||
on:click={() => (showHidden = true)}
|
||||
>
|
||||
<i class="ri-arrow-down-s-line" />
|
||||
<span class="txt">Show all ({totalHidden})</span>
|
||||
</button>
|
||||
{#if enabledProviders.length > 0}
|
||||
<hr />
|
||||
{/if}
|
||||
|
||||
<div class="flex m-t-base">
|
||||
<div class="flex-fill" />
|
||||
{#if hasChanges}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-transparent btn-hint"
|
||||
disabled={isSaving}
|
||||
on:click={() => reset()}
|
||||
>
|
||||
<span class="txt">Cancel</span>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-expanded"
|
||||
class:btn-loading={isSaving}
|
||||
disabled={!hasChanges || isSaving}
|
||||
>
|
||||
<span class="txt">Save changes</span>
|
||||
</button>
|
||||
<div class="grid grid-sm">
|
||||
{#each disabledProviders as provider (provider.key)}
|
||||
<div class="col-lg-6">
|
||||
<AuthProviderCard {provider} bind:config={formSettings[provider.key]} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
|
||||
@@ -6,23 +6,17 @@
|
||||
</script>
|
||||
|
||||
<div class="section-title">Azure AD endpoints</div>
|
||||
<div class="grid">
|
||||
<div class="col-lg-12">
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} required={config.enabled} />
|
||||
<div class="help-block">
|
||||
Eg. {`https://login.microsoftonline.com/YOUR_DIRECTORY_TENANT_ID/oauth2/v2.0/authorize`}
|
||||
</div>
|
||||
</Field>
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} required={config.enabled} />
|
||||
<div class="help-block">
|
||||
Eg. {`https://login.microsoftonline.com/YOUR_DIRECTORY_TENANT_ID/oauth2/v2.0/authorize`}
|
||||
</div>
|
||||
<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="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>
|
||||
</Field>
|
||||
</Field>
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.tokenUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Token URL</label>
|
||||
<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>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
@@ -6,26 +6,18 @@
|
||||
</script>
|
||||
|
||||
<div class="section-title">Endpoints</div>
|
||||
<div class="grid">
|
||||
<div class="col-lg-12">
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} required={config.enabled} />
|
||||
<div class="help-block">Eg. https://example.com/authorize/</div>
|
||||
</Field>
|
||||
</div>
|
||||
<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="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="url" id={uniqueId} bind:value={config.userApiUrl} required={config.enabled} />
|
||||
<div class="help-block">Eg. https://example.com/userinfo/</div>
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} required={config.enabled} />
|
||||
<div class="help-block">Eg. https://example.com/authorize/</div>
|
||||
</Field>
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.tokenUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Token URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.tokenUrl} required={config.enabled} />
|
||||
<div class="help-block">Eg. https://example.com/token/</div>
|
||||
</Field>
|
||||
<Field class="form-field {config.enabled ? 'required' : ''}" name="{key}.userApiUrl" let:uniqueId>
|
||||
<label for={uniqueId}>User API URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.userApiUrl} required={config.enabled} />
|
||||
<div class="help-block">Eg. https://example.com/userinfo/</div>
|
||||
</Field>
|
||||
|
||||
@@ -6,23 +6,15 @@
|
||||
</script>
|
||||
|
||||
<div class="section-title">Selfhosted endpoints (optional)</div>
|
||||
<div class="grid">
|
||||
<div class="col-lg-4">
|
||||
<Field class="form-field" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} />
|
||||
</Field>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<Field class="form-field" name="{key}.tokenUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Token URL</label>
|
||||
<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="url" id={uniqueId} bind:value={config.userApiUrl} />
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
<Field class="form-field" name="{key}.authUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Auth URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.authUrl} />
|
||||
</Field>
|
||||
<Field class="form-field" name="{key}.tokenUrl" let:uniqueId>
|
||||
<label for={uniqueId}>Token URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.tokenUrl} />
|
||||
</Field>
|
||||
<Field class="form-field" name="{key}.userApiUrl" let:uniqueId>
|
||||
<label for={uniqueId}>User API URL</label>
|
||||
<input type="url" id={uniqueId} bind:value={config.userApiUrl} />
|
||||
</Field>
|
||||
|
||||
+64
-48
@@ -5,92 +5,108 @@ import AppleOptions from "@/components/settings/providers/AppleOptions.svel
|
||||
|
||||
// Object list with all supported OAuth2 providers in the format:
|
||||
// ```
|
||||
// { settingsKey: { title, icon, hidden, optionsComponent? } }
|
||||
// [ { key, title, logo, optionsComponent? }, ... ]
|
||||
// ```
|
||||
//
|
||||
// If `optionsComponent` is provided it will receive 2 parameters:
|
||||
// - `key` - the provider settings key (eg. "gitlabAuth")
|
||||
// - `config` - the provider settings config that is currently being updated
|
||||
export default {
|
||||
appleAuth: {
|
||||
export default [
|
||||
{
|
||||
key: "appleAuth",
|
||||
title: "Apple",
|
||||
icon: "ri-apple-fill",
|
||||
logo: "/images/oauth2/apple.svg",
|
||||
optionsComponent: AppleOptions,
|
||||
},
|
||||
googleAuth: {
|
||||
{
|
||||
key: "googleAuth",
|
||||
title: "Google",
|
||||
icon: "ri-google-fill",
|
||||
logo: "/images/oauth2/google.svg",
|
||||
},
|
||||
microsoftAuth: {
|
||||
{
|
||||
key: "facebookAuth",
|
||||
title: "Facebook",
|
||||
logo: "/images/oauth2/facebook.svg",
|
||||
},
|
||||
{
|
||||
key: "microsoftAuth",
|
||||
title: "Microsoft",
|
||||
icon: "ri-microsoft-fill",
|
||||
logo: "/images/oauth2/microsoft.svg",
|
||||
optionsComponent: MicrosoftOptions,
|
||||
},
|
||||
facebookAuth: {
|
||||
title: "Facebook",
|
||||
icon: "ri-facebook-fill",
|
||||
},
|
||||
twitterAuth: {
|
||||
title: "Twitter",
|
||||
icon: "ri-twitter-fill",
|
||||
},
|
||||
githubAuth: {
|
||||
{
|
||||
key: "githubAuth",
|
||||
title: "GitHub",
|
||||
icon: "ri-github-fill",
|
||||
logo: "/images/oauth2/github.svg",
|
||||
},
|
||||
gitlabAuth: {
|
||||
{
|
||||
key: "gitlabAuth",
|
||||
title: "GitLab",
|
||||
icon: "ri-gitlab-fill",
|
||||
logo: "/images/oauth2/gitlab.svg",
|
||||
optionsComponent: SelfHostedOptions,
|
||||
},
|
||||
giteeAuth: {
|
||||
{
|
||||
key: "giteeAuth",
|
||||
title: "Gitee",
|
||||
icon: "ri-git-repository-fill",
|
||||
logo: "/images/oauth2/gitee.svg",
|
||||
},
|
||||
giteaAuth: {
|
||||
{
|
||||
key: "giteaAuth",
|
||||
title: "Gitea",
|
||||
icon: "ri-cup-fill",
|
||||
logo: "/images/oauth2/gitea.svg",
|
||||
optionsComponent: SelfHostedOptions,
|
||||
},
|
||||
discordAuth: {
|
||||
{
|
||||
key: "discordAuth",
|
||||
title: "Discord",
|
||||
icon: "ri-discord-fill",
|
||||
logo: "/images/oauth2/discord.svg",
|
||||
},
|
||||
kakaoAuth: {
|
||||
{
|
||||
key: "twitterAuth",
|
||||
title: "Twitter",
|
||||
logo: "/images/oauth2/twitter.svg",
|
||||
},
|
||||
{
|
||||
key: "kakaoAuth",
|
||||
title: "Kakao",
|
||||
icon: "ri-kakao-talk-fill",
|
||||
logo: "/images/oauth2/kakao.svg",
|
||||
},
|
||||
spotifyAuth: {
|
||||
{
|
||||
key: "spotifyAuth",
|
||||
title: "Spotify",
|
||||
icon: "ri-spotify-fill",
|
||||
logo: "/images/oauth2/spotify.svg",
|
||||
},
|
||||
twitchAuth: {
|
||||
{
|
||||
key: "twitchAuth",
|
||||
title: "Twitch",
|
||||
icon: "ri-twitch-fill",
|
||||
logo: "/images/oauth2/twitch.svg",
|
||||
},
|
||||
stravaAuth: {
|
||||
{
|
||||
key: "stravaAuth",
|
||||
title: "Strava",
|
||||
icon: "ri-riding-fill",
|
||||
logo: "/images/oauth2/strava.svg",
|
||||
},
|
||||
livechatAuth: {
|
||||
{
|
||||
key: "livechatAuth",
|
||||
title: "LiveChat",
|
||||
icon: "ri-chat-1-fill",
|
||||
logo: "/images/oauth2/livechat.svg",
|
||||
},
|
||||
oidcAuth: {
|
||||
title: "OpenID Connect - Authentik, Keycloak, Okta, etc.",
|
||||
icon: "ri-lock-fill",
|
||||
{
|
||||
key: "oidcAuth",
|
||||
title: "OpenID Connect",
|
||||
logo: "/images/oauth2/oidc.svg",
|
||||
optionsComponent: OIDCOptions,
|
||||
},
|
||||
oidc2Auth: {
|
||||
title: "(2) OpenID Connect - Authentik, Keycloak, Okta, etc.",
|
||||
icon: "ri-lock-fill",
|
||||
hidden: true,
|
||||
{
|
||||
key: "oidc2Auth",
|
||||
title: "(2) OpenID Connect",
|
||||
logo: "/images/oauth2/oidc.svg",
|
||||
optionsComponent: OIDCOptions,
|
||||
},
|
||||
oidc3Auth: {
|
||||
title: "(3) OpenID Connect - Authentik, Keycloak, Okta, etc.",
|
||||
icon: "ri-lock-fill",
|
||||
hidden: true,
|
||||
{
|
||||
key: "oidc3Auth",
|
||||
title: "(3) OpenID Connect",
|
||||
logo: "/images/oauth2/oidc.svg",
|
||||
optionsComponent: OIDCOptions,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user