Coverage for fastapi/security/api_key.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-12-04 08:29 +0000

1from typing import Optional, Union 1abcdefg

2 

3from annotated_doc import Doc 1abcdefg

4from fastapi.openapi.models import APIKey, APIKeyIn 1abcdefg

5from fastapi.security.base import SecurityBase 1abcdefg

6from starlette.exceptions import HTTPException 1abcdefg

7from starlette.requests import Request 1abcdefg

8from starlette.status import HTTP_401_UNAUTHORIZED 1abcdefg

9from typing_extensions import Annotated 1abcdefg

10 

11 

12class APIKeyBase(SecurityBase): 1abcdefg

13 def __init__( 1abcdefg

14 self, 

15 location: APIKeyIn, 

16 name: str, 

17 description: Union[str, None], 

18 scheme_name: Union[str, None], 

19 auto_error: bool, 

20 ): 

21 self.auto_error = auto_error 1abcdefg

22 

23 self.model: APIKey = APIKey( 1abcdefg

24 **{"in": location}, 

25 name=name, 

26 description=description, 

27 ) 

28 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdefg

29 

30 def make_not_authenticated_error(self) -> HTTPException: 1abcdefg

31 """ 

32 The WWW-Authenticate header is not standardized for API Key authentication but 

33 the HTTP specification requires that an error of 401 "Unauthorized" must 

34 include a WWW-Authenticate header. 

35 

36 Ref: https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized 

37 

38 For this, this method sends a custom challenge `APIKey`. 

39 """ 

40 return HTTPException( 1hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW

41 status_code=HTTP_401_UNAUTHORIZED, 

42 detail="Not authenticated", 

43 headers={"WWW-Authenticate": "APIKey"}, 

44 ) 

45 

46 def check_api_key(self, api_key: Optional[str]) -> Optional[str]: 1abcdefg

47 if not api_key: 2+ h , i - X . j / k : Y ; l = m ? Z @ n [ o ] 0 ^ p _ q ` 1 { r | s } 2 ~ t abu bb3 cbv dbw eb4 fbx gby hb5 ibz jbA kb6 lbB mbC nb7 obD pbE qb8 rbF sbG tb9 ubH vbI wb! xbJ ybK zb# AbL BbM Cb$ DbN EbO Fb% GbP HbQ Ib' JbR KbS Lb( MbT NbU Ob) PbV QbW Rb*

48 if self.auto_error: 1hiXjkYlmZno0pq1rs2tu3vw4xy5zA6BC7DE8FG9HI!JK#LM$NO%PQ'RS(TU)VW*

49 raise self.make_not_authenticated_error() 1hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW

50 return None 1XYZ0123456789!#$%'()*

51 return api_key 2+ , - . / : ; = ? @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLbMbNbObPbQbRb

52 

53 

54class APIKeyQuery(APIKeyBase): 1abcdefg

55 """ 

56 API key authentication using a query parameter. 

57 

58 This defines the name of the query parameter that should be provided in the request 

59 with the API key and integrates that into the OpenAPI documentation. It extracts 

60 the key value sent in the query parameter automatically and provides it as the 

61 dependency result. But it doesn't define how to send that API key to the client. 

62 

63 ## Usage 

64 

65 Create an instance object and use that object as the dependency in `Depends()`. 

66 

67 The dependency result will be a string containing the key value. 

68 

69 ## Example 

70 

71 ```python 

72 from fastapi import Depends, FastAPI 

73 from fastapi.security import APIKeyQuery 

74 

75 app = FastAPI() 

76 

77 query_scheme = APIKeyQuery(name="api_key") 

78 

79 

80 @app.get("/items/") 

81 async def read_items(api_key: str = Depends(query_scheme)): 

82 return {"api_key": api_key} 

83 ``` 

84 """ 

85 

86 def __init__( 1abcdefg

87 self, 

88 *, 

89 name: Annotated[ 

90 str, 

91 Doc("Query parameter name."), 

92 ], 

93 scheme_name: Annotated[ 

94 Optional[str], 

95 Doc( 

96 """ 

97 Security scheme name. 

98 

99 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

100 """ 

101 ), 

102 ] = None, 

103 description: Annotated[ 

104 Optional[str], 

105 Doc( 

106 """ 

107 Security scheme description. 

108 

109 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

110 """ 

111 ), 

112 ] = None, 

113 auto_error: Annotated[ 

114 bool, 

115 Doc( 

116 """ 

117 By default, if the query parameter is not provided, `APIKeyQuery` will 

118 automatically cancel the request and send the client an error. 

119 

120 If `auto_error` is set to `False`, when the query parameter is not 

121 available, instead of erroring out, the dependency result will be 

122 `None`. 

123 

124 This is useful when you want to have optional authentication. 

125 

126 It is also useful when you want to have authentication that can be 

127 provided in one of multiple optional ways (for example, in a query 

128 parameter or in an HTTP Bearer token). 

129 """ 

130 ), 

131 ] = True, 

132 ): 

133 super().__init__( 1abcdefg

134 location=APIKeyIn.query, 

135 name=name, 

136 scheme_name=scheme_name, 

137 description=description, 

138 auto_error=auto_error, 

139 ) 

140 

141 async def __call__(self, request: Request) -> Optional[str]: 1abcdefg

142 api_key = request.query_params.get(self.model.name) 2; l = m ? Z { r | s } 2 fbx gby hb5 obD pbE qb8 xbJ ybK zb# GbP HbQ Ib' PbV QbW Rb*

143 return self.check_api_key(api_key) 2; l = m ? Z { r | s } 2 fbx gby hb5 obD pbE qb8 xbJ ybK zb# GbP HbQ Ib' PbV QbW Rb*

144 

145 

146class APIKeyHeader(APIKeyBase): 1abcdefg

147 """ 

148 API key authentication using a header. 

149 

150 This defines the name of the header that should be provided in the request with 

151 the API key and integrates that into the OpenAPI documentation. It extracts 

152 the key value sent in the header automatically and provides it as the dependency 

153 result. But it doesn't define how to send that key to the client. 

154 

155 ## Usage 

156 

157 Create an instance object and use that object as the dependency in `Depends()`. 

158 

159 The dependency result will be a string containing the key value. 

160 

161 ## Example 

162 

163 ```python 

164 from fastapi import Depends, FastAPI 

165 from fastapi.security import APIKeyHeader 

166 

167 app = FastAPI() 

168 

169 header_scheme = APIKeyHeader(name="x-key") 

170 

171 

172 @app.get("/items/") 

173 async def read_items(key: str = Depends(header_scheme)): 

174 return {"key": key} 

175 ``` 

176 """ 

177 

178 def __init__( 1abcdefg

179 self, 

180 *, 

181 name: Annotated[str, Doc("Header name.")], 

182 scheme_name: Annotated[ 

183 Optional[str], 

184 Doc( 

185 """ 

186 Security scheme name. 

187 

188 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

189 """ 

190 ), 

191 ] = None, 

192 description: Annotated[ 

193 Optional[str], 

194 Doc( 

195 """ 

196 Security scheme description. 

197 

198 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

199 """ 

200 ), 

201 ] = None, 

202 auto_error: Annotated[ 

203 bool, 

204 Doc( 

205 """ 

206 By default, if the header is not provided, `APIKeyHeader` will 

207 automatically cancel the request and send the client an error. 

208 

209 If `auto_error` is set to `False`, when the header is not available, 

210 instead of erroring out, the dependency result will be `None`. 

211 

212 This is useful when you want to have optional authentication. 

213 

214 It is also useful when you want to have authentication that can be 

215 provided in one of multiple optional ways (for example, in a header or 

216 in an HTTP Bearer token). 

217 """ 

218 ), 

219 ] = True, 

220 ): 

221 super().__init__( 1abcdefg

222 location=APIKeyIn.header, 

223 name=name, 

224 scheme_name=scheme_name, 

225 description=description, 

226 auto_error=auto_error, 

227 ) 

228 

229 async def __call__(self, request: Request) -> Optional[str]: 1abcdefg

230 api_key = request.headers.get(self.model.name) 2. j / k : Y ^ p _ q ` 1 cbv dbw eb4 lbB mbC nb7 ubH vbI wb! DbN EbO Fb% MbT NbU Ob)

231 return self.check_api_key(api_key) 2. j / k : Y ^ p _ q ` 1 cbv dbw eb4 lbB mbC nb7 ubH vbI wb! DbN EbO Fb% MbT NbU Ob)

232 

233 

234class APIKeyCookie(APIKeyBase): 1abcdefg

235 """ 

236 API key authentication using a cookie. 

237 

238 This defines the name of the cookie that should be provided in the request with 

239 the API key and integrates that into the OpenAPI documentation. It extracts 

240 the key value sent in the cookie automatically and provides it as the dependency 

241 result. But it doesn't define how to set that cookie. 

242 

243 ## Usage 

244 

245 Create an instance object and use that object as the dependency in `Depends()`. 

246 

247 The dependency result will be a string containing the key value. 

248 

249 ## Example 

250 

251 ```python 

252 from fastapi import Depends, FastAPI 

253 from fastapi.security import APIKeyCookie 

254 

255 app = FastAPI() 

256 

257 cookie_scheme = APIKeyCookie(name="session") 

258 

259 

260 @app.get("/items/") 

261 async def read_items(session: str = Depends(cookie_scheme)): 

262 return {"session": session} 

263 ``` 

264 """ 

265 

266 def __init__( 1abcdefg

267 self, 

268 *, 

269 name: Annotated[str, Doc("Cookie name.")], 

270 scheme_name: Annotated[ 

271 Optional[str], 

272 Doc( 

273 """ 

274 Security scheme name. 

275 

276 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

277 """ 

278 ), 

279 ] = None, 

280 description: Annotated[ 

281 Optional[str], 

282 Doc( 

283 """ 

284 Security scheme description. 

285 

286 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

287 """ 

288 ), 

289 ] = None, 

290 auto_error: Annotated[ 

291 bool, 

292 Doc( 

293 """ 

294 By default, if the cookie is not provided, `APIKeyCookie` will 

295 automatically cancel the request and send the client an error. 

296 

297 If `auto_error` is set to `False`, when the cookie is not available, 

298 instead of erroring out, the dependency result will be `None`. 

299 

300 This is useful when you want to have optional authentication. 

301 

302 It is also useful when you want to have authentication that can be 

303 provided in one of multiple optional ways (for example, in a cookie or 

304 in an HTTP Bearer token). 

305 """ 

306 ), 

307 ] = True, 

308 ): 

309 super().__init__( 1abcdefg

310 location=APIKeyIn.cookie, 

311 name=name, 

312 scheme_name=scheme_name, 

313 description=description, 

314 auto_error=auto_error, 

315 ) 

316 

317 async def __call__(self, request: Request) -> Optional[str]: 1abcdefg

318 api_key = request.cookies.get(self.model.name) 2+ h , i - X @ n [ o ] 0 ~ t abu bb3 ibz jbA kb6 rbF sbG tb9 AbL BbM Cb$ JbR KbS Lb(

319 return self.check_api_key(api_key) 2+ h , i - X @ n [ o ] 0 ~ t abu bb3 ibz jbA kb6 rbF sbG tb9 AbL BbM Cb$ JbR KbS Lb(