Skip to content

Commit

Permalink
Add auth config for allowed_redirect_urls
Browse files Browse the repository at this point in the history
  • Loading branch information
scotttrinh committed Nov 6, 2023
1 parent e89a5e1 commit b1a6ac5
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
1 change: 1 addition & 0 deletions shared/studio/tabs/auth/authAdmin.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
min-width: 0;
max-width: 100%;

textarea,
input {
font-family: inherit;
line-height: inherit;
Expand Down
73 changes: 73 additions & 0 deletions shared/studio/tabs/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,54 @@ const ConfigPage = observer(function ConfigPage() {
</div>
</div>
</div>

<div className={styles.gridItem}>
<div className={styles.configName}>allowed_redirect_urls</div>
<div className={styles.configInputWrapper}>
<div className={styles.configInput}>
{state.configData ? (
<>
<TextArea
value={
state.draftAllowedRedirectUrls.value ??
state.configData.allowed_redirect_urls
}
onChange={(urls) => state.draftAllowedRedirectUrls.setValue(urls)}
error={state.draftAllowedRedirectUrls.error}
size={32.5}
/>
{state.draftAllowedRedirectUrls.value != null ? (
<>
<Button
className={styles.button}
label={
state.draftTokenTime.updating ? "Updating" : "Update"
}
disabled={!!state.draftAllowedRedirectUrls.error}
loading={state.draftAllowedRedirectUrls.updating}
onClick={() => state.draftAllowedRedirectUrls.update()}
/>
<Button
className={styles.button}
label="Cancel"
onClick={() => state.draftAllowedRedirectUrls.setValue(null)}
/>
</>
) : null}
</>
) : (
"loading..."
)}
</div>
<div className={styles.configExplain}>
New line seperated list of URLs that will be checked against
this list to ensure they are going to a trusted domain controlled
by the application. URLs are matched based on checking if the
candidate redirect URL is a match or a subdirectory of any of
these allowed URLs
</div>
</div>
</div>
</div>

<div className={styles.header}>Providers</div>
Expand Down Expand Up @@ -853,3 +901,28 @@ function Input({
</div>
);
}

function TextArea({
value,
onChange,
error,
size,
}: {
value: string;
onChange: (val: string) => void;
error?: string | null;
size?: number;
}) {
return (
<div className={styles.inputWrapper}>
<div className={cn(styles.input, {[styles.error]: !!error})}>
<textarea
value={value}
onChange={(e) => onChange(e.target.value)}
style={{height: 22 * (value?.split("\n").length ?? 1) + 10 + "px", width: size ? `${size}ch` : undefined}}
/>
</div>
{error ? <div className={styles.inputErrorMessage}>{error}</div> : null}
</div>
);
}
2 changes: 2 additions & 0 deletions shared/studio/tabs/auth/loginuipreview.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
opacity: 0.7;
}

form textarea,
form input {
border-radius: 8px;
border: 1px solid #dee2e6;
Expand Down Expand Up @@ -196,6 +197,7 @@
color: #dee2e6;
}

form textarea,
form input {
border-color: #495057;
background: #31373d;
Expand Down
27 changes: 25 additions & 2 deletions shared/studio/tabs/auth/state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {AppleIcon, AzureIcon, GithubIcon, GoogleIcon} from "../icons";
export interface AuthConfigData {
signing_key_exists: boolean;
token_time_to_live: string;
allowed_redirect_urls: string;
}

export type OAuthProviderData = {
Expand Down Expand Up @@ -107,6 +108,25 @@ export class AuthAdminState extends Model({
return /^\d+$/.test(dur) ? null : "Invalid duration";
}
),
draftAllowedRedirectUrls: createDraftAuthConfig(
"allowed_redirect_urls",
"std::str",
(urls) => {
if (urls === null) return null;

const urlList = urls.split("\n").filter((str) => str.trim() !== "");
if (urlList.length > 128) {
return "Too many URLs, maximum supported number of URLs is 128."
}

return null;
},
(urls) => {
if (urls === null) return null;
const urlList = urls.split("\n").filter((str) => str.trim() !== "");
return `{${urlList.map((u) => `'${u}'`).join(", ")}}`;
}
),

draftProviderConfig: prop<DraftProviderConfig | null>(null),
draftUIConfig: prop<DraftUIConfig | null>(null),
Expand Down Expand Up @@ -183,6 +203,7 @@ export class AuthAdminState extends Model({
select cfg::Config.extensions[is AuthConfig] {
signing_key_exists := signing_key_exists(),
token_time_to_live_seconds := <str>duration_get(.token_time_to_live, 'totalseconds'),
allowed_redirect_urls,
providers: {
_typename := .__type__.name,
name,
Expand Down Expand Up @@ -211,6 +232,7 @@ export class AuthAdminState extends Model({
this.configData = {
signing_key_exists: data.signing_key_exists,
token_time_to_live: data.token_time_to_live_seconds,
allowed_redirect_urls: data.allowed_redirect_urls.join("\n"),
};
this.providers = data.providers;
this.uiConfig = data.ui ?? false;
Expand Down Expand Up @@ -420,7 +442,8 @@ export class DraftProviderConfig extends Model({
function createDraftAuthConfig(
name: string,
type: string,
validate: (val: string | null) => string | null
validate: (val: string | null) => string | null,
transform: (val: string | null) => string | null = JSON.stringify
) {
@model(`DraftAuthConfig/${name}`)
class DraftAuthConfig extends Model({
Expand All @@ -447,7 +470,7 @@ function createDraftAuthConfig(
await conn.execute(
`
configure current database set
ext::auth::AuthConfig::${name} := <${type}>${JSON.stringify(this.value)}`
ext::auth::AuthConfig::${name} := <${type}>${transform(this.value)}`
);
await state.refreshConfig();
this.setValue(null);
Expand Down

0 comments on commit b1a6ac5

Please sign in to comment.