Skip to content

Commit

Permalink
Add support for webauthn provider
Browse files Browse the repository at this point in the history
  • Loading branch information
jaclarke committed Feb 8, 2024
1 parent 5eb694c commit 3bb78b1
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 23 deletions.
41 changes: 40 additions & 1 deletion shared/studio/tabs/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
SMTPSecurity,
smtpSecurity,
_providersInfo,
LocalWebAuthnProviderData,
} from "./state";

import {encodeB64} from "edgedb/dist/primitives/buffer";
Expand Down Expand Up @@ -1009,6 +1010,29 @@ const DraftProviderConfigForm = observer(function DraftProviderConfigForm({
</>
) : providerKind === "Local" ? (
<div className={styles.gridItem}>
{draftState.selectedProviderType ===
"ext::auth::WebAuthnProviderConfig" ? (
<div className={styles.gridItem}>
<div className={styles.configName}>relying_party_origin</div>
<div className={styles.configInputWrapper}>
<div className={styles.configInput}>
<Input
size={32}
value={draftState.webauthnRelyingOrigin}
onChange={(val) =>
draftState.setWebauthnRelyingOrigin(val)
}
error={draftState.webauthnRelyingOriginError}
/>
</div>
<div className={styles.configExplain}>
The full origin of the sign-in page including protocol and
port of the application. If using the built-in UI, this
should be the origin of the EdgeDB server.
</div>
</div>
</div>
) : null}
<div className={styles.configName}>require_verification</div>
<div className={styles.configInputWrapper}>
<div className={styles.configInput}>
Expand Down Expand Up @@ -1113,12 +1137,27 @@ function ProviderCard({provider}: {provider: AuthProviderData}) {
</>
) : kind === "Local" ? (
<>
{provider.name === "builtin::local_webauthn" ? (
<>
<div className={styles.providerConfigName}>
relying_party_origin
</div>
<div className={styles.providerConfigValue}>
{
(provider as LocalWebAuthnProviderData)
.relying_party_origin
}
</div>
</>
) : null}
<div className={styles.providerConfigName}>
require_verification
</div>
<div className={styles.providerConfigValue}>
{(
provider as LocalEmailPasswordProviderData
provider as
| LocalEmailPasswordProviderData
| LocalWebAuthnProviderData
).require_verification.toString()}
</div>
</>
Expand Down
84 changes: 62 additions & 22 deletions shared/studio/tabs/auth/state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@ export type LocalEmailPasswordProviderData = {
_typename: "ext::auth::EmailPasswordProviderConfig";
require_verification: boolean;
};
export type LocalWebAuthnProviderData = {
name: string;
_typename: "ext::auth::WebAuthnProviderConfig";
relying_party_origin: string;
require_verification: boolean;
};
export type AuthProviderData =
| OAuthProviderData
| LocalEmailPasswordProviderData;
| LocalEmailPasswordProviderData
| LocalWebAuthnProviderData;

export interface AuthUIConfigData {
redirect_to: string;
Expand Down Expand Up @@ -121,6 +128,11 @@ export const _providersInfo: {
displayName: "Email + Password",
icon: <></>,
},
"ext::auth::WebAuthnProviderConfig": {
kind: "Local",
displayName: "WebAuthn",
icon: <></>,
},
};

export type ProviderTypename = keyof typeof _providersInfo;
Expand Down Expand Up @@ -297,7 +309,11 @@ export class AuthAdminState extends Model({
name,
[is OAuthProviderConfig].client_id,
[is OAuthProviderConfig].additional_scope,
[is EmailPasswordProviderConfig].require_verification,
require_verification := (
[is EmailPasswordProviderConfig].require_verification ??
[is WebAuthnProviderConfig].require_verification
),
[is WebAuthnProviderConfig].relying_party_origin
},
ui: {
redirect_to,
Expand Down Expand Up @@ -631,6 +647,8 @@ export class DraftProviderConfig extends Model({
oauthSecret: prop("").withSetter(),
additionalScope: prop("").withSetter(),

webauthnRelyingOrigin: prop("").withSetter(),

requireEmailVerification: prop(true).withSetter(),
}) {
@computed
Expand All @@ -643,13 +661,23 @@ export class DraftProviderConfig extends Model({
return this.oauthSecret.trim() === "" ? "Secret is required" : null;
}

@computed
get webauthnRelyingOriginError() {
return this.webauthnRelyingOrigin.trim() === ""
? "Relying origin is required"
: null;
}

@computed
get formValid(): boolean {
switch (_providersInfo[this.selectedProviderType].kind) {
case "OAuth":
return !this.oauthClientIdError && !this.oauthSecretError;
case "Local":
return true;
return this.selectedProviderType ===
"ext::auth::WebAuthnProviderConfig"
? !this.webauthnRelyingOriginError
: true;
}
}

Expand All @@ -672,28 +700,40 @@ export class DraftProviderConfig extends Model({
try {
const provider = _providersInfo[this.selectedProviderType];

const queryFields: string[] = [];
if (provider.kind === "OAuth") {
queryFields.push(
`client_id := ${JSON.stringify(this.oauthClientId)}`,
`secret := ${JSON.stringify(this.oauthSecret)}`
);
if (this.additionalScope.trim()) {
queryFields.push(
`additional_scope := ${JSON.stringify(
this.additionalScope.trim()
)}`
);
}
} else if (provider.kind === "Local") {
if (
this.selectedProviderType === "ext::auth::WebAuthnProviderConfig"
) {
queryFields.push(
`relying_party_origin := ${JSON.stringify(
this.webauthnRelyingOrigin
)}`
);
}
queryFields.push(
`require_verification := ${
this.requireEmailVerification ? "true" : "false"
}`
);
}

await conn.execute(
`configure current database
insert ${this.selectedProviderType} {
${
provider.kind === "OAuth"
? `
client_id := ${JSON.stringify(this.oauthClientId)},
secret := ${JSON.stringify(this.oauthSecret)},
${
this.additionalScope.trim()
? `additional_scope := ${JSON.stringify(
this.additionalScope.trim()
)}`
: ""
}
`
: provider.kind === "Local"
? `require_verification := ${
this.requireEmailVerification ? "true" : "false"
},`
: ""
}
${queryFields.join(",\n")}
}`
);
await state.refreshConfig();
Expand Down

0 comments on commit 3bb78b1

Please sign in to comment.