re-add authentication section

This commit is contained in:
Malte Teichert
2024-05-20 18:38:22 +02:00
parent d4fd0f2fbe
commit a0cc87c96c
6 changed files with 234 additions and 268 deletions

15
src/app.d.ts vendored
View File

@@ -7,3 +7,18 @@ declare namespace App {
// interface Error {}
// interface Platform {}
}
declare namespace Oauth2 {
interface Oauth2Flow {
authorizationUrl: string;
scopes: Record<string, string>;
refreshUrl?: string;
}
interface Oauth2FlowTemplates {
implicit: Oauth2Flow;
password: Oauth2Flow;
clientCredentials: Oauth2Flow;
authorizationCode: Oauth2Flow & { tokenUrl: string };
}
}

View File

@@ -1,71 +1,49 @@
import type {
ApiKeyAuth,
BasicAuth,
BearerAuth,
CookieAuth,
OAuth2Auth,
OpenIdConnectAuth
} from './types/auth';
import type { OpenAPIV3_1 } from './openAPITypes';
export const basicAuthTemplate: BasicAuth = {
identifier: '',
export const basicAuthTemplate: OpenAPIV3_1.HttpSecurityScheme = {
type: 'http',
scheme: 'basic'
scheme: 'basic',
description: undefined
};
export const bearerAuthTemplate: BearerAuth = {
identifier: '',
export const bearerAuthTemplate: OpenAPIV3_1.HttpSecurityScheme = {
type: 'http',
scheme: 'bearer',
bearerFormat: ''
bearerFormat: undefined,
description: undefined
};
export const apiKeyAuthTemplate: ApiKeyAuth = {
identifier: '',
export const apiKeyAuthTemplate: OpenAPIV3_1.ApiKeySecurityScheme = {
type: 'apiKey',
in: 'header',
name: ''
in: 'header', // or 'query' or 'cookie'
name: '',
description: undefined
};
export const openIdAuthTemplate: OpenIdConnectAuth = {
identifier: '',
export const openIdAuthTemplate: OpenAPIV3_1.OpenIdSecurityScheme = {
type: 'openIdConnect',
openIdConnectUrl: ''
openIdConnectUrl: '',
description: undefined
};
export const oauth2AuthTemplate: OAuth2Auth = {
identifier: '',
export const oauth2AuthTemplate: OpenAPIV3_1.OAuth2SecurityScheme = {
type: 'oauth2',
description: '',
flows: []
flows: {},
description: undefined
};
export const cookieAuthTemplate: CookieAuth = {
identifier: '',
type: 'apiKey',
in: 'cookie',
name: ''
const baseOauth2Flow: Oauth2.Oauth2Flow = {
authorizationUrl: '',
scopes: {},
refreshUrl: undefined
};
export const oauth2FlowTemplates = {
export const oauth2FlowTemplates: Oauth2.Oauth2FlowTemplates = {
implicit: baseOauth2Flow,
password: baseOauth2Flow,
clientCredentials: baseOauth2Flow,
authorizationCode: {
name: 'authorizationCode',
authorizationUrl: '',
tokenUrl: '',
scopes: []
},
implicit: {
name: 'implicit',
authorizationUrl: '',
scopes: []
},
password: {
name: 'password',
tokenUrl: '',
scopes: []
},
clientCredentials: {
name: 'clientCredentials',
...baseOauth2Flow,
tokenUrl: ''
}
};

View File

@@ -1,169 +1,129 @@
<script lang="ts">
import { oauth2FlowTemplates } from '$lib/authTemplates';
import type { SecuritySchema } from '$lib/types';
import OAuthFlow from './OAuthFlow.svelte';
import type { OpenAPIV3_1 } from '$lib/openAPITypes';
import OAuthFlow from '$lib/components/atoms/OAuthFlow.svelte';
export let data: SecuritySchema;
export let schema: OpenAPIV3_1.SecuritySchemeObject;
let oauthFlow: string;
const addOAuthFlow = () => {
if (!data.hasOwnProperty('flows')) {
return;
}
const tempData = data;
let newFlow;
let availableFlows: string[] = ['implicit', 'password', 'clientCredentials', 'authorizationCode'];
switch (oauthFlow) {
case 'authorizationCode':
newFlow = oauth2FlowTemplates.authorizationCode;
break;
case 'implicit':
newFlow = oauth2FlowTemplates.implicit;
break;
case 'password':
newFlow = oauth2FlowTemplates.password;
break;
case 'clientCredentials':
newFlow = oauth2FlowTemplates.clientCredentials;
break;
default:
newFlow = undefined;
break;
}
// remove flows that are already in Object.keys(schema.flows)
// @ts-expect-error - security schema definition is lacking a bit
availableFlows = availableFlows.filter((flow) => !Object.keys(schema.flows).includes(flow));
if (newFlow) {
// @ts-expect-error - this is only called when data.flows exists
tempData.flows.push(newFlow);
data = tempData;
}
let flowType: string;
const addOauthFlow = () => {
if (!flowType) return;
// @ts-expect-error - security schema definition is lacking a bit
schema.flows[flowType] = oauth2FlowTemplates[flowType];
// remove used flow from availableFlows
availableFlows = availableFlows.filter((flow) => flow !== flowType);
};
const removeFlow = (index: number) => {
if (!data.hasOwnProperty('flows')) {
return;
}
let tempData = data;
// @ts-expect-error - this is only called when data.flows exists
tempData.flows.splice(index, 1);
data = tempData;
const removeOauthFlow = (flow: string) => {
// @ts-expect-error - security schema definition is lacking a bit
let tempFlows = schema.flows;
delete tempFlows[flow];
// @ts-expect-error - security schema definition is lacking a bit
schema.flows = tempFlows;
// add flow back to availableFlows
availableFlows = [...availableFlows, flow];
};
</script>
<div class="flex flex-col gap-2"></div>
<div class="space-y-4">
{#if data.type == 'http' && data.scheme == 'basic'}
<h3 class="h3">Basic Auth</h3>
<p>Basic authentication is a simple authentication scheme built into the HTTP protocol.</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="basicAuth" bind:value={data.identifier} />
</label>
{:else if data.type == 'http' && data.scheme == 'bearer'}
<h3 class="h3">Bearer Auth</h3>
<p>Basic authentication is a simple authentication scheme built into the HTTP protocol.</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="bearerAuth" bind:value={data.identifier} />
</label>
<label class="space-y-2">
<h4 class="h4">Format: arbitrary string for documentation purposes</h4>
<input class="input" type="text" placeholder="JWT" bind:value={data.identifier} />
</label>
{:else if data.type == 'apiKey' && data.in == 'header'}
<!-- Cookie API Keys handled by CookieAuth -->
<h3 class="h3">API Key</h3>
<div class="space-y-2">
{#if schema.type === 'http' && schema.scheme === 'basic'}
<h3 class="h3">Basic Authentication</h3>
<p>
API keys are a simple authentication method that the client provides when making API requests.
Basic authentication is a simple authentication scheme built into the HTTP protocol. No
configuration required.
</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="apiKeyAuth" bind:value={data.identifier} />
</label>
<label class="space-y-2">
<h4 class="h4">Location</h4>
<select class="input">
<option value="header" selected>Header</option>
<option value="query">Query</option>
</select>
</label>
<label class="space-y-2">
<h4 class="h4">Name (required)</h4>
<input class="input" type="text" placeholder="X-API-Key" />
</label>
{:else if data.type == 'openIdConnect'}
<h3 class="h3">OpenID</h3>
{:else if schema.type === 'http' && schema.scheme === 'bearer'}
<h3 class="h3">Bearer Authentication</h3>
<p>
OpenID Connect (OIDC) is an identity layer built on top of the OAuth 2.0 protocol and
supported by some OAuth 2.0 providers, such as Google and Azure Active Directory.
Bearer authentication (also called token authentication) is an HTTP authentication scheme that
involves security tokens called bearer tokens.
</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="openIdAuth" bind:value={data.identifier} />
<label>
<h5 class="h5">Description</h5>
<p class="text-sm">Human-readable information. May contain Markdown.</p>
<textarea class="textarea" placeholder="Description" />
</label>
<label>
<h4 class="h4">OpenID Connect URL</h4>
<h5 class="h5">Bearer format</h5>
<p class="text-sm">A hint to the client to identify how the bearer token is formatted.</p>
<input type="text" class="input" placeholder="JWT" />
</label>
{:else if schema.type === 'apiKey'}
<h3 class="h3">API Key Authentication</h3>
<label>
<h5 class="h5">Location</h5>
<select class="input">
<option value="header">header</option>
<option value="query">query</option>
<option value="cookie">cookie</option>
</select>
</label>
<label>
<h5 class="h5">Name</h5>
<p class="text-sm">The name of the key parameter in the location.</p>
<input type="text" class="input" placeholder="api_key" />
</label>
<label>
<h5 class="h5">Description</h5>
<p class="text-sm">Human-readable information. May contain Markdown.</p>
<textarea class="textarea" placeholder="Description" />
</label>
{:else if schema.type === 'openIdConnect'}
<h3 class="h3">OpenID Connect Authentication</h3>
<label>
<h5 class="h5">OpenID Connect URL</h5>
<p class="text-sm">The URL must point to a JSON OpenID Connect Discovery document.</p>
<input
class="input"
type="url"
class="input"
placeholder="https://example.com/.well-known/openid-configuration"
/>
</label>
{:else if data.type == 'oauth2'}
<h3 class="h3">OAuth2</h3>
<p>
OAuth 2.0 is an authorization protocol that gives an API client limited access to user data on
a web server.
</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="oAuth" bind:value={data.identifier} />
<label>
<h5 class="h5">Description</h5>
<p class="text-sm">Human-readable information. May contain Markdown.</p>
<textarea class="textarea" placeholder="Description" />
</label>
<label class="space-y-2">
<h4 class="h4">Description</h4>
<textarea
class="input rounded-container-token"
placeholder="OAuth2 Authorization Description"
/>
{:else if schema.type === 'oauth2'}
<h3 class="h3">Oauth2 Authentication</h3>
<label>
<h5 class="h5">Description</h5>
<p class="text-sm">Human-readable information. May contain Markdown.</p>
<textarea class="textarea" placeholder="Description" />
</label>
{#each data.flows as flow, index}
<OAuthFlow bind:flow />
<span class="flex justify-center">
<h5 class="h5">Flows</h5>
{#each Object.keys(schema.flows) as flow}
<div class="flex w-full justify-between items-center">
<h6 class="h6">{flow.charAt(0).toLocaleUpperCase() + flow.slice(1)}</h6>
<button
type="button"
class="btn variant-ringed-error hover:variant-filled-error"
on:click={() => {
removeFlow(index);
}}
class="btn btn-sm variant-ringed-error hover:variant-filled-error"
on:click={() => removeOauthFlow(flow)}
>
Remove Flow
Remove {flow}
</button>
</span>
<hr />
</div>
<OAuthFlow type={flow} flow={schema.flows[flow]} />
{/each}
<span class="w-full flex justify-center gap-2">
<select class="input w-min" bind:value={flowType}>
{#each availableFlows as flow}
<option value={flow}>{flow}</option>
{/each}
<span class="flex justify-center items-center gap-2 max-w-md mx-auto">
<select class="input" bind:value={oauthFlow}>
<option value="authorizationCode">Authorization Code</option>
<option value="implicit">Implicit</option>
<option value="password">Password</option>
<option value="clientCredentials">Client Credentials</option>
</select>
<button type="button" class="btn variant-filled-primary" on:click={addOAuthFlow}>
Add OAuth2 Flow
<button type="button" class="btn btn-sm variant-filled-primary" on:click={addOauthFlow}>
Add Flow
</button>
</span>
{:else if data.type == 'apiKey' && data.in == 'cookie'}
<h3 class="h3">Cookie Auth</h3>
<p>
Cookie authentication uses HTTP cookies to authenticate client requests and maintain session
information.
</p>
<label class="space-y-2">
<h4 class="h4">Unique name for the security scheme</h4>
<input class="input" type="text" placeholder="cookieAuth" bind:value={data.identifier} />
</label>
<label class="space-y-2">
<h4 class="h4">Cookie Name (required)</h4>
<input class="input" type="text" placeholder="JSESSIONID" />
</label>
{/if}
</div>

View File

@@ -1,23 +1,20 @@
<script lang="ts">
import ScopeList from './ScopeList.svelte';
import type { Flows } from '$lib/types/auth';
export let flow: Flows;
export let type: 'implicit' | 'authorizationCode' | 'password' | 'clientCredentials';
export let flow: Oauth2.Oauth2Flow | (Oauth2.Oauth2Flow & { tokenUrl: string });
const addScope = () => {
flow.scopes = [...flow.scopes, [{ scope: '', description: '' }]];
};
const removeScope = (index: number) => {
flow.scopes = flow.scopes.filter((_, i) => i !== index);
const name = prompt('Enter scope');
if (!name) return;
flow.scopes[name] = '';
};
const removeScope = (index: number) => {};
</script>
<div class="flex flex-col gap-4">
{#if flow.name == 'authorizationCode'}
<h3 class="h3">Authorization Code</h3>
<label class="space-y-2">
<h4 class="h4">Authorization URL</h4>
<div class="border-token rounded-container-token p-4">
<div class="ml-4 flex flex-col gap-4">
<label>
<h5 class="h5">Authorization URL</h5>
<p class="text-sm">The authorization URL to be used for this flow.</p>
<input
type="text"
name="authorizationURL"
@@ -26,8 +23,10 @@
bind:value={flow.authorizationUrl}
/>
</label>
<label class="space-y-2">
<h4 class="h4">Token URL</h4>
{#if type === 'authorizationCode'}
<label>
<h5 class="h5">Token URL</h5>
<p class="text-sm">The token URL to be used for this flow.</p>
<input
type="text"
name="tokenURL"
@@ -36,45 +35,60 @@
bind:value={flow.tokenUrl}
/>
</label>
<ScopeList bind:flow />
{:else if flow.name == 'implicit'}
<h3 class="h3">Implicit</h3>
<label class="space-y-2">
<h4 class="h4">Authorization URL</h4>
<input
type="text"
name="authorizationURL"
class="input"
placeholder="https://api.example.com/oauth2/authorize"
bind:value={flow.authorizationUrl}
/>
</label>
<ScopeList bind:flow />
{:else if flow.name == 'password'}
<h3 class="h3">Password</h3>
<label class="space-y-2">
<h4 class="h4">Token URL</h4>
<input
type="text"
name="tokenURL"
class="input"
placeholder="https://api.example.com/oauth2/token"
bind:value={flow.tokenUrl}
/>
</label>
<ScopeList bind:flow />
{:else if flow.name == 'clientCredentials'}
<h3 class="h3">Client Credentials</h3>
<label class="space-y-2">
<h4 class="h4">Token URL</h4>
<input
type="text"
name="tokenURL"
class="input"
placeholder="https://api.example.com/oauth2/token"
bind:value={flow.tokenUrl}
/>
</label>
<ScopeList bind:flow />
{/if}
<label>
<h5 class="h5">Refresh URL</h5>
<p class="text-sm">The refresh URL to be used for this flow. (optional)</p>
<input
type="text"
name="refreshURL"
class="input"
placeholder="https://api.example.com/oauth2/refresh"
bind:value={flow.refreshUrl}
/>
</label>
<label>
<h5 class="h5">Scopes</h5>
<p class="text-sm">The available scopes for this flow.</p>
<table class="table">
<tbody>
{#each Object.keys(flow.scopes) as scope, index}
<tr>
<td class="!text-lg">
{scope}
</td>
<td class="w-full">
<input
type="text"
name="scope"
class="input"
placeholder="description of the scope"
bind:value={flow.scopes[scope]}
/>
</td>
<td>
<button
type="button"
class="btn variant-ringed-error hover:variant-filled-error"
on:click={() => removeScope(index)}
>
Remove
</button>
</td>
</tr>
{/each}
</tbody>
</table>
<span class="w-full flex justify-center">
<button
type="button"
class="btn btn-sm variant-filled-primary"
class:mt-2={Object.keys(flow.scopes).length !== 0}
on:click={addScope}
>
Add Scope
</button>
</span>
</label>
</div>
</div>

View File

@@ -1,10 +1,9 @@
<!-- <script lang="ts">
<script lang="ts">
import { openApiStore } from '$lib';
import {
apiKeyAuthTemplate,
basicAuthTemplate,
bearerAuthTemplate,
cookieAuthTemplate,
oauth2AuthTemplate,
openIdAuthTemplate
} from '$lib/authTemplates';
@@ -12,7 +11,7 @@
let selectedSchema: string;
const addSecuritySchema = () => {
let tempSchemaList = $openApiStore.securitySchemas;
let tempSchemaList = $openApiStore.security;
let newSchema;
switch (selectedSchema) {
case 'basicAuth':
@@ -30,9 +29,6 @@
case 'oAuthSample':
newSchema = oauth2AuthTemplate;
break;
case 'cookieAuth':
newSchema = cookieAuthTemplate;
break;
default:
newSchema = undefined;
break;
@@ -40,30 +36,34 @@
if (newSchema) {
tempSchemaList = [...tempSchemaList, newSchema];
$openApiStore.securitySchemas = tempSchemaList;
$openApiStore.security = tempSchemaList;
}
};
const removeSecuritySchema = (index: number) => {
let tempSchemaList = $openApiStore.securitySchemas;
let tempSchemaList = $openApiStore.security;
tempSchemaList.splice(index, 1);
$openApiStore.securitySchemas = tempSchemaList;
$openApiStore.security = tempSchemaList;
};
</script>
<form class="container mx-auto card px-6 py-4 space-y-4">
{#each $openApiStore.securitySchemas as schema, index}
<AuthenticationItem bind:data={schema} />
<span class="flex justify-center">
<form
class="container mx-auto border-token rounded-container-token bg-surface-backdrop-token px-6 py-4 space-y-4"
>
{#each $openApiStore.security as schema, index}
<div class="card w-full p-4">
<div class="flex flex-row-reverse w-full">
<button
type="button"
class="btn variant-ringed-error hover:variant-filled-error"
class="btn btn-sm variant-ringed-error hover:variant-filled-error"
on:click={() => {
removeSecuritySchema(index);
}}
>
Remove Security Schema
Remove schema
</button>
</span>
</div>
<AuthenticationItem bind:schema />
</div>
<hr />
{/each}
@@ -74,10 +74,9 @@
<option value="ApiKeyAuth">API Key Auth</option>
<option value="openId">OpenID</option>
<option value="oAuthSample">OAuth2</option>
<option value="cookieAuth">Cookie Auth</option>
</select>
<button type="button" class="btn variant-filled-primary" on:click={addSecuritySchema}>
Add Security Schema
</button>
</span>
</form> -->
</form>

View File

@@ -29,7 +29,7 @@
{#if $tabSet === 0}
<Info />
{:else if $tabSet === 1}
<!-- <Authentication /> -->
<Authentication />
{:else if $tabSet === 2}
<Servers />
{:else if $tabSet === 3}