certified.fast — FastAPI Integration
Requires the http extra (pip install 'certified[http]').
Provides FastAPI dependencies for extracting peer certificate info and authorizing requests via biscuit tokens.
Peer certificate dependencies
get_peercert
FastAPI dependency for returning client cert. information
Example return
{"subject":[[["commonName","Charles T. User"]]], "issuer":[[["commonName","Charles T. User"]], [["pseudonym","Signing Certificate"]]], "version":3, "serialNumber":"03A2", "notBefore":"Sep 12 05:48:44 2024 GMT", "notAfter":"Sep 12 05:48:44 2025 GMT", "subjectAltName":[["email","hello@localhost"], ["DNS","localhost"], ["IP Address","127.0.0.1"]] }
Source code in certified/fast.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
get_remote_addr
Source code in certified/fast.py
26 27 28 29 | |
get_clientname
A helper to return either (in priority order)
- uid:{userid of client}
- cn:{common name of client}
- addr:{ip addr. of client}
Source code in certified/fast.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
name_from_peer
Source code in certified/fast.py
58 59 60 61 62 63 64 65 66 67 68 69 | |
PeerCert and ClientName are Annotated type aliases suitable for use
as FastAPI dependency parameters:
from certified.fast import PeerCert, ClientName
@app.get("/info")
async def info(peer: PeerCert, name: ClientName):
return {"name": name, "cert": peer}
Biscuit token issuance
Baker
This class provides a "get_token" method to prepare (bake) a token for a user.
It should be used to create a "/token" endpoint as follows:
from certified.fast import Baker cert = Certified() baker = Baker(cert.signer()) app.post("/token")(baker.get_token)
Source code in certified/fast.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
get_token(client, hours=24.0)
Returns a biscuit certifying the user identity and token lifetime.
Applications should supply this as their "/token" endpoint
Source code in certified/fast.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
Biscuit token authorization
BiscuitAuthz
For additional help on using biscuit attributes and checks, see docs/authz.md
Instances of this class are callables which will check (critique) a token for a particular purpose.
It may be used directly as follows:
from biscuit_auth import PublicKey from certified.fast import BiscuitAuthz app_name = name pubkey = lambda i: PublicKey.from_bytes( "authorizer pubkey" ) DefaultAuthz = Annotated[bool, BiscuitAuthz(app_name, pubkey)] async def get_info(info: str, authz: DefaultAuthz): # authz is always True here return info
This will allow use of the get_info endpoint only if the caller provides a valid biscuit that passes all its own internal restrictions.
Note this is a parameterized dependency. See https://fastapi.tiangolo.com/advanced/advanced-dependencies
Source code in certified/fast.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
Critic
Returns an Authorizer, which can be called with a custom authorizer to add authentication to your FastAPI endpoint.
The following example creates a list of accepted public keys, then creates a custom authorizer which adds a check, and then calls the rest of the auth chain. When creating an API route, Authz is a handy dependency to call this new auth chain.
from biscuit_auth import PublicKey, AuthorizerBuilder, Biscuit from certified.fast import Critic, run_authz pubkeys = [PublicKey("authorizer pubkey1"), PublicKey("authorizer pubkey2")] def custom(authorizer: AuthorizerBuilder, token: Biscuit) -> bool: authorizer.add_code(...) return run_authz(authorizer, token) Authz = Critic("frontend app name", pubkeys) async def post_config(info: str, authz: Annotated[bool, Authz(custom)): # authz is a check that is always True (or else FastAPI would have raised 403 already) do_something_with(info)
Since Authz("admin:write") has created a dependency
(of type BiscuitAuthz(app, pubkeys, "admin:write")),
that dependency can gather data from:
- the client certificate, providing client({id})
- the URL accessed, providing path({path}) and operation({method})
- the call header, where a "Biscuit: b64_encoded_biscuit" is
required
It then throws an HTTP Unauthorized/401 if a biscuit is missing, Forbidden/403 if the biscuit auth fails, or else returns True otherwise.
Source code in certified/fast.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | |
run_authz
Default is to call auth.build(token).authorize() and ignore revocation id-s.
This will ensure all the biscuit's (client-controlled) checks pass, but not enforce any specific security requirements from the server's side other than that the certificate names a user.
Source code in certified/fast.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |