1/**
2 * Defines security scheme declarations for declarative HTTP APIs.
3 *
4 * Security schemes describe where credentials are read from and which credential
5 * type is passed to security middleware. They are consumed by
6 * `HttpApiMiddleware.Service`, `HttpApiBuilder`, generated clients, and OpenAPI
7 * generation, but they do not authenticate requests by themselves.
8 *
9 * @stability unstable
10 * @since 4.0.0
11 */
12import * as Context from "../Context.ts"
13import { dual } from "../Function.ts"
14import { type Pipeable, pipeArguments } from "../Pipeable.ts"
15import type { Redacted } from "../Redacted.ts"
16import type { Covariant } from "../Types.ts"
17
18const TypeId = "~effect/http-api/HttpApiSecurity"
19
20/**
21 * Union of security schemes supported by the HTTP API OpenAPI model.
22 *
23 * @stability unstable
24 * @category models
25 * @since 4.0.0
26 */
27export type HttpApiSecurity = Http | ApiKey | Basic
28
29/**
30 * Helper types for HTTP API security schemes.
31 *
32 * @stability unstable
33 * @since 4.0.0
34 */
35export declare namespace HttpApiSecurity {
36 /**
37 * Common prototype for security schemes, carrying the credential type and OpenAPI annotations.
38 *
39 * @stability unstable
40 * @category models
41 * @since 4.0.0
42 */
43 export interface Proto<out A> extends Pipeable {
44 readonly [TypeId]: {
45 readonly _A: Covariant<A>
46 }
47 readonly annotations: Context.Context<never>
48 }
49
50 /**
51 * Extracts the credential type produced by a security scheme.
52 *
53 * @stability unstable
54 * @category models
55 * @since 4.0.0
56 */
57 export type Type<A extends HttpApiSecurity> = A extends Proto<infer Out> ? Out : never
58}
59
60/**
61 * Http token security scheme whose decoded credential is a redacted token.
62 *
63 * @stability unstable
64 * @category models
65 * @since 4.0.0
66 */
67export interface Http extends HttpApiSecurity.Proto<Redacted> {
68 readonly _tag: "Http"
69 readonly scheme: string
70 /**
71 * @internal
72 */
73 readonly schemeLength: number
74}
75
76/**
77 * API key security scheme identifying the key name and whether it is read from a header, query parameter, or cookie.
78 *
79 * @stability unstable
80 * @category models
81 * @since 4.0.0
82 */
83export interface ApiKey extends HttpApiSecurity.Proto<Redacted> {
84 readonly _tag: "ApiKey"
85 readonly in: "header" | "query" | "cookie"
86 readonly key: string
87}
88
89/**
90 * HTTP Basic authentication security scheme whose decoded credential is `Credentials`.
91 *
92 * @stability unstable
93 * @category models
94 * @since 4.0.0
95 */
96export interface Basic extends HttpApiSecurity.Proto<Credentials> {
97 readonly _tag: "Basic"
98}
99
100/**
101 * Decoded credentials for HTTP Basic authentication.
102 *
103 * @stability unstable
104 * @category models
105 * @since 4.0.0
106 */
107export interface Credentials {
108 readonly username: string
109 readonly password: Redacted
110}
111
112const Proto = {
113 [TypeId]: TypeId,
114 pipe() {
115 return pipeArguments(this, arguments)
116 }
117}
118
119/**
120 * Creates a Http token security scheme.
121 *
122 * **When to use**
123 *
124 * Use to require `Authorization: scheme ...` credentials for an HTTP API group
125 * or endpoint.
126 *
127 * **Details**
128 *
129 * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this
130 * security scheme.
131 *
132 * @see {@link apiKey} for an API-key security scheme
133 * @see {@link basic} for an HTTP Basic security scheme
134 * @stability unstable
135 * @category constructors
136 * @since 4.0.0
137 */
138export const http = (options: {
139 readonly scheme: string
140}): Http =>
141 Object.assign(Object.create(Proto), {
142 _tag: "Http",
143 scheme: options.scheme,
144 schemeLength: options.scheme.length,
145 annotations: Context.empty()
146 })
147
148/**
149 * Creates a Bearer token security scheme.
150 *
151 * **When to use**
152 *
153 * Use to require `Authorization: Bearer ...` credentials for an HTTP API group
154 * or endpoint.
155 *
156 * **Details**
157 *
158 * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this
159 * security scheme.
160 *
161 * @see {@link apiKey} for an API-key security scheme
162 * @see {@link basic} for an HTTP Basic security scheme
163 * @stability unstable
164 * @category constructors
165 * @since 4.0.0
166 */
167export const bearer: Http = http({ scheme: "Bearer" })
168
169/**
170 * Creates an API key security scheme.
171 *
172 * **When to use**
173 *
174 * Use to require API key credentials passed through a header, query parameter,
175 * or cookie.
176 *
177 * **Details**
178 *
179 * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this
180 * security scheme.
181 *
182 * Use `HttpApiBuilder.securitySetCookie` to set the correct cookie in a
183 * handler. By default, `in` is `"header"`.
184 *
185 * @see {@link bearer} for a Bearer token security scheme
186 * @see {@link basic} for an HTTP Basic security scheme
187 * @stability unstable
188 * @category constructors
189 * @since 4.0.0
190 */
191export const apiKey = (options: {
192 readonly key: string
193 readonly in?: "header" | "query" | "cookie" | undefined
194}): ApiKey =>
195 Object.assign(Object.create(Proto), {
196 _tag: "ApiKey",
197 key: options.key,
198 in: options.in ?? "header",
199 annotations: Context.empty()
200 })
201
202/**
203 * Creates an HTTP Basic authentication security scheme.
204 *
205 * **When to use**
206 *
207 * Use to require HTTP Basic username/password credentials.
208 *
209 * **Details**
210 *
211 * Use `HttpApiBuilder.middlewareSecurity` to implement API middleware for this
212 * security scheme.
213 *
214 * @see {@link bearer} for a Bearer token security scheme
215 * @see {@link apiKey} for an API-key security scheme
216 * @stability unstable
217 * @category constructors
218 * @since 4.0.0
219 */
220export const basic: Basic = Object.assign(Object.create(Proto), {
221 _tag: "Basic",
222 annotations: Context.empty()
223})
224
225/**
226 * Merges OpenAPI annotations into a security scheme.
227 *
228 * @stability unstable
229 * @category annotations
230 * @since 4.0.0
231 */
232export const annotateMerge: {
233 <I>(annotations: Context.Context<I>): <A extends HttpApiSecurity>(self: A) => A
234 <A extends HttpApiSecurity, I>(self: A, annotations: Context.Context<I>): A
235} = dual(
236 2,
237 <A extends HttpApiSecurity, I>(self: A, annotations: Context.Context<I>): A =>
238 Object.assign(Object.create(Proto), {
239 ...self,
240 annotations: Context.merge(self.annotations, annotations)
241 })
242)
243
244/**
245 * Adds an OpenAPI annotation value to a security scheme.
246 *
247 * @stability unstable
248 * @category annotations
249 * @since 4.0.0
250 */
251export const annotate: {
252 <I, S>(service: Context.Key<I, S>, value: S): <A extends HttpApiSecurity>(self: A) => A
253 <A extends HttpApiSecurity, I, S>(self: A, service: Context.Key<I, S>, value: S): A
254} = dual(
255 3,
256 <A extends HttpApiSecurity, I, S>(self: A, service: Context.Key<I, S>, value: S): A =>
257 Object.assign(Object.create(Proto), {
258 ...self,
259 annotations: Context.add(self.annotations, service, value)
260 })
261)