diff --git a/src/app.css b/src/app.css
index 6ea048d..c1051bb 100644
--- a/src/app.css
+++ b/src/app.css
@@ -73,3 +73,8 @@ a[target='_blank']::after {
position: absolute;
}
}
+
+ul {
+ padding-left: 1.45rem;
+ margin: 0;
+}
diff --git a/src/lib/components/avatar/AvatarCard.svelte b/src/lib/components/avatar/AvatarCard.svelte
index 04f3c00..b2e6da7 100644
--- a/src/lib/components/avatar/AvatarCard.svelte
+++ b/src/lib/components/avatar/AvatarCard.svelte
@@ -37,7 +37,6 @@
height: 120px;
width: 120px;
flex: 0 0 120px;
- background-color: var(--in-normalized-background);
&.small {
height: 60px;
diff --git a/src/lib/components/form/FormActions.svelte b/src/lib/components/form/FormActions.svelte
new file mode 100644
index 0000000..22b0359
--- /dev/null
+++ b/src/lib/components/form/FormActions.svelte
@@ -0,0 +1,26 @@
+
+
+
+ {@render children?.()}
+
+
+
diff --git a/src/lib/server/oauth2/model/client.ts b/src/lib/server/oauth2/model/client.ts
index 1ec657d..0997469 100644
--- a/src/lib/server/oauth2/model/client.ts
+++ b/src/lib/server/oauth2/model/client.ts
@@ -20,6 +20,7 @@ import { UserTokens, Users } from '$lib/server/users';
import type { OAuth2ClientInfo, PaginationMeta } from '$lib/types';
import { and, count, eq, like, or, sql } from 'drizzle-orm';
import { createLocalJWKSet, exportJWK, importJWK, jwtVerify, type JWK } from 'jose';
+import { ensureArray } from '$lib/utils';
export enum OAuth2ClientURLType {
REDIRECT_URI = 'redirect_uri',
@@ -203,7 +204,15 @@ export class OAuth2Clients {
return scope;
}
- return scope.includes(',') ? scope.split(',').map((item) => item.trim()) : scope.split(' ');
+ const delimeterSplit = scope.includes(',') ? scope.split(',') : scope.split(' ');
+ return delimeterSplit.reduce((list, scope) => {
+ const trimmed = scope.trim();
+ if (!!trimmed) {
+ list.push(trimmed);
+ }
+
+ return list;
+ }, []);
}
static joinScope(scope: string[]): string {
@@ -549,7 +558,7 @@ export class OAuth2Clients {
});
// Check audience, token must be intended for our service
- const checkAudience = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
+ const checkAudience = ensureArray(payload.aud);
if (
!checkAudience.some(
(entry) => entry?.startsWith(env.PUBLIC_URL) || entry?.startsWith(privateEnv.JWT_ISSUER)
diff --git a/src/lib/server/oauth2/model/tokens.ts b/src/lib/server/oauth2/model/tokens.ts
index 9aa60ba..50be229 100644
--- a/src/lib/server/oauth2/model/tokens.ts
+++ b/src/lib/server/oauth2/model/tokens.ts
@@ -168,7 +168,7 @@ export class OAuth2Codes {
const user = await Users.getById(userId);
const accessToken = CryptoUtils.generateString(64);
- const scopes = (!Array.isArray(scope) ? OAuth2Clients.splitScope(scope) : scope).join(' ');
+ const scopes = OAuth2Clients.joinScope(OAuth2Clients.splitScope(scope));
const expiresAt = new Date(Date.now() + ttl * 1000);
const pcke =
@@ -238,7 +238,7 @@ export class OAuth2AccessTokens {
const user = userId != null ? await Users.getById(userId) : undefined;
const accessToken = CryptoUtils.generateString(128);
- const scopes = (!Array.isArray(scope) ? OAuth2Clients.splitScope(scope) : scope).join(' ');
+ const scopes = OAuth2Clients.joinScope(OAuth2Clients.splitScope(scope));
const expiresAt = new Date(Date.now() + ttl * 1000);
@@ -298,7 +298,7 @@ export class OAuth2RefreshTokens {
const user = await Users.getById(userId);
const accessToken = CryptoUtils.generateString(64);
- const scopes = (!Array.isArray(scope) ? OAuth2Clients.splitScope(scope) : scope).join(' ');
+ const scopes = OAuth2Clients.joinScope(OAuth2Clients.splitScope(scope));
const expiresAt = new Date(Date.now() + OAuth2Tokens.refreshTtl * 1000);
@@ -350,7 +350,7 @@ export class OAuth2DeviceCodes {
const userCode =
`${CryptoUtils.generateString(3)}-${CryptoUtils.generateString(3)}`.toUpperCase();
- const scopes = (!Array.isArray(scope) ? OAuth2Clients.splitScope(scope) : scope).join(' ');
+ const scopes = OAuth2Clients.joinScope(OAuth2Clients.splitScope(scope));
const expiresAt = new Date(Date.now() + OAuth2Tokens.deviceTtl * 1000);
await OAuth2Tokens.insert(
@@ -442,7 +442,7 @@ export class OAuth2ParCodes {
const client = await OAuth2Clients.fetchById(clientId);
const parCode = CryptoUtils.generateString(32);
- const scopes = (!Array.isArray(scope) ? OAuth2Clients.splitScope(scope) : scope).join(' ');
+ const scopes = OAuth2Clients.joinScope(OAuth2Clients.splitScope(scope));
const expiresAt = new Date(Date.now() + OAuth2Tokens.parTtl * 1000);
const pcke =
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 87f212e..676680b 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -14,6 +14,12 @@ export const hasPrivileges = (list: string[], privileges: RequiredPrivileges) =>
return list.includes(item);
});
+/**
+ * Wait until the `check` function returns a truthy value.
+ * @param check Condition to run
+ * @param checkInterval Interval to run condition (ms)
+ * @param checkTimeout Timeout of the check (ms)
+ */
export const waitIsTruthy = (check: () => boolean, checkInterval = 500, checkTimeout = 5000) => {
let time = 0;
return new Promise((resolve, reject) => {
@@ -46,3 +52,10 @@ export function self) => void
}
};
}
+
+/**
+ * Ensures that the given input is an array.
+ * @param input Array or single value
+ * @returns Array
+ */
+export const ensureArray = (input: T | T[]): T[] => (Array.isArray(input) ? input : [input]);
diff --git a/src/routes/ssoadmin/audit/+page.svelte b/src/routes/ssoadmin/audit/+page.svelte
index c69d323..3d3b45a 100644
--- a/src/routes/ssoadmin/audit/+page.svelte
+++ b/src/routes/ssoadmin/audit/+page.svelte
@@ -9,8 +9,8 @@
import SplitView from '$lib/components/container/SplitView.svelte';
import Button from '$lib/components/Button.svelte';
import AdminAuditCard from '$lib/components/admin/AdminAuditCard.svelte';
- import { hasPrivileges } from '$lib/utils';
import FormActions from '$lib/components/form/FormActions.svelte';
+ import { hasPrivileges } from '$lib/utils';
interface Props {
data: PageData;
diff --git a/svelte.config.js b/svelte.config.js
index 08420be..cf6c387 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -16,6 +16,10 @@ const config = {
// This is reimplemented in hooks.server.ts to allow certain endpoints
csrf: {
checkOrigin: false
+ },
+
+ router: {
+ resolution: 'server'
}
}
};