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

45 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-08 03:53 +0000

1from typing import Optional 1abcde

2 

3from fastapi.openapi.models import APIKey, APIKeyIn 1abcde

4from fastapi.security.base import SecurityBase 1abcde

5from starlette.exceptions import HTTPException 1abcde

6from starlette.requests import Request 1abcde

7from starlette.status import HTTP_403_FORBIDDEN 1abcde

8from typing_extensions import Annotated, Doc 1abcde

9 

10 

11class APIKeyBase(SecurityBase): 1abcde

12 pass 1abcde

13 

14 

15class APIKeyQuery(APIKeyBase): 1abcde

16 """ 

17 API key authentication using a query parameter. 

18 

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

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

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

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

23 

24 ## Usage 

25 

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

27 

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

29 

30 ## Example 

31 

32 ```python 

33 from fastapi import Depends, FastAPI 

34 from fastapi.security import APIKeyQuery 

35 

36 app = FastAPI() 

37 

38 query_scheme = APIKeyQuery(name="api_key") 

39 

40 

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

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

43 return {"api_key": api_key} 

44 ``` 

45 """ 

46 

47 def __init__( 1abcde

48 self, 

49 *, 

50 name: Annotated[ 

51 str, 

52 Doc("Query parameter name."), 

53 ], 

54 scheme_name: Annotated[ 

55 Optional[str], 

56 Doc( 

57 """ 

58 Security scheme name. 

59 

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

61 """ 

62 ), 

63 ] = None, 

64 description: Annotated[ 

65 Optional[str], 

66 Doc( 

67 """ 

68 Security scheme description. 

69 

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

71 """ 

72 ), 

73 ] = None, 

74 auto_error: Annotated[ 

75 bool, 

76 Doc( 

77 """ 

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

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

80 

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

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

83 `None`. 

84 

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

86 

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

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

89 parameter or in an HTTP Bearer token). 

90 """ 

91 ), 

92 ] = True, 

93 ): 

94 self.model: APIKey = APIKey( 1abcde

95 **{"in": APIKeyIn.query}, # type: ignore[arg-type] 

96 name=name, 

97 description=description, 

98 ) 

99 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde

100 self.auto_error = auto_error 1abcde

101 

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

103 api_key = request.query_params.get(self.model.name) 1abcde

104 if not api_key: 1abcde

105 if self.auto_error: 1abcde

106 raise HTTPException( 1abcde

107 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" 

108 ) 

109 else: 

110 return None 1abcde

111 return api_key 1abcde

112 

113 

114class APIKeyHeader(APIKeyBase): 1abcde

115 """ 

116 API key authentication using a header. 

117 

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

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

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

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

122 

123 ## Usage 

124 

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

126 

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

128 

129 ## Example 

130 

131 ```python 

132 from fastapi import Depends, FastAPI 

133 from fastapi.security import APIKeyHeader 

134 

135 app = FastAPI() 

136 

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

138 

139 

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

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

142 return {"key": key} 

143 ``` 

144 """ 

145 

146 def __init__( 1abcde

147 self, 

148 *, 

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

150 scheme_name: Annotated[ 

151 Optional[str], 

152 Doc( 

153 """ 

154 Security scheme name. 

155 

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

157 """ 

158 ), 

159 ] = None, 

160 description: Annotated[ 

161 Optional[str], 

162 Doc( 

163 """ 

164 Security scheme description. 

165 

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

167 """ 

168 ), 

169 ] = None, 

170 auto_error: Annotated[ 

171 bool, 

172 Doc( 

173 """ 

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

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

176 

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

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

179 

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

181 

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

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

184 in an HTTP Bearer token). 

185 """ 

186 ), 

187 ] = True, 

188 ): 

189 self.model: APIKey = APIKey( 1abcde

190 **{"in": APIKeyIn.header}, # type: ignore[arg-type] 

191 name=name, 

192 description=description, 

193 ) 

194 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde

195 self.auto_error = auto_error 1abcde

196 

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

198 api_key = request.headers.get(self.model.name) 1abcde

199 if not api_key: 1abcde

200 if self.auto_error: 1abcde

201 raise HTTPException( 1abcde

202 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" 

203 ) 

204 else: 

205 return None 1abcde

206 return api_key 1abcde

207 

208 

209class APIKeyCookie(APIKeyBase): 1abcde

210 """ 

211 API key authentication using a cookie. 

212 

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

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

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

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

217 

218 ## Usage 

219 

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

221 

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

223 

224 ## Example 

225 

226 ```python 

227 from fastapi import Depends, FastAPI 

228 from fastapi.security import APIKeyCookie 

229 

230 app = FastAPI() 

231 

232 cookie_scheme = APIKeyCookie(name="session") 

233 

234 

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

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

237 return {"session": session} 

238 ``` 

239 """ 

240 

241 def __init__( 1abcde

242 self, 

243 *, 

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

245 scheme_name: Annotated[ 

246 Optional[str], 

247 Doc( 

248 """ 

249 Security scheme name. 

250 

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

252 """ 

253 ), 

254 ] = None, 

255 description: Annotated[ 

256 Optional[str], 

257 Doc( 

258 """ 

259 Security scheme description. 

260 

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

262 """ 

263 ), 

264 ] = None, 

265 auto_error: Annotated[ 

266 bool, 

267 Doc( 

268 """ 

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

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

271 

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

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

274 

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

276 

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

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

279 in an HTTP Bearer token). 

280 """ 

281 ), 

282 ] = True, 

283 ): 

284 self.model: APIKey = APIKey( 1abcde

285 **{"in": APIKeyIn.cookie}, # type: ignore[arg-type] 

286 name=name, 

287 description=description, 

288 ) 

289 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde

290 self.auto_error = auto_error 1abcde

291 

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

293 api_key = request.cookies.get(self.model.name) 1abcde

294 if not api_key: 1abcde

295 if self.auto_error: 1abcde

296 raise HTTPException( 1abcde

297 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" 

298 ) 

299 else: 

300 return None 1abcde

301 return api_key 1abcde