Files
fastapi-toolsets/v4.0/module/security/index.html
T

2488 lines
106 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Production-ready utilities for FastAPI applications.">
<meta name="author" content="d3vyce">
<link rel="canonical" href="https://fastapi-toolsets.d3vyce.fr/v4.0/module/security/">
<link rel="prev" href="../schemas/">
<link rel="next" href="../../reference/cli/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="zensical-0.0.40">
<title>Security - FastAPI Toolsets</title>
<link rel="stylesheet" href="../../assets/stylesheets/modern/main.fba56155.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/modern/palette.dfe2e883.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,500,500i,700,700i%7CJetbrains+Mono:400,400i,700,700i&amp;display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"Jetbrains Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_scope.pathname.endsWith("/")||(__md_scope=new URL(__md_scope.pathname+"/",location)),__md_hash=e=>[...e].reduce(((e,t)=>(e<<5)-e+t.charCodeAt(0)),0),__md_get=(e,t=localStorage,_=__md_scope)=>JSON.parse(t.getItem(_.pathname+"."+e)),__md_set=(e,t,_=localStorage,a=__md_scope)=>{try{_.setItem(a.pathname+"."+e,JSON.stringify(t))}catch(e){}},document.documentElement.setAttribute("data-platform",navigator.platform)</script>
<script
defer
src="https://analytics.d3vyce.fr/script.js"
data-website-id="338b8816-7b99-4c6a-82f3-15595be3fd47"
></script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer" aria-label="Navigation"></label>
<div data-md-component="skip">
<a href="#security" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<div data-md-color-scheme="default" data-md-component="outdated" hidden>
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="FastAPI Toolsets" class="md-header__button md-logo" aria-label="FastAPI Toolsets" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer" aria-label="Navigation">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-menu" viewBox="0 0 24 24"><path d="M4 5h16M4 12h16M4 19h16"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
FastAPI Toolsets
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Security
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="none" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
</label>
<input class="md-option" data-md-color-media="none" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-moon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search" aria-label="Search">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-search" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog" aria-label="Search">
<button type="button" class="md-search__button">
Search
</button>
</div>
<div class="md-header__source">
<a href="https://github.com/d3vyce/fastapi-toolsets" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2026 Fonticons, Inc.--><path fill="currentColor" d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../cli/" class="md-tabs__link">
Modules
</a>
</li>
<li class="md-tabs__item">
<a href="../../reference/cli/" class="md-tabs__link">
Reference
</a>
</li>
<li class="md-tabs__item">
<a href="../../examples/pagination-search/" class="md-tabs__link">
Examples
</a>
</li>
<li class="md-tabs__item">
<a href="../../migration/v4/" class="md-tabs__link">
Migration
</a>
</li>
<li class="md-tabs__item">
<a href="https://github.com/d3vyce/fastapi-toolsets/releases" class="md-tabs__link">
Changelog ↗
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="FastAPI Toolsets" class="md-nav__button md-logo" aria-label="FastAPI Toolsets" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
</a>
FastAPI Toolsets
</label>
<div class="md-nav__source">
<a href="https://github.com/d3vyce/fastapi-toolsets" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2026 Fonticons, Inc.--><path fill="currentColor" d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Modules
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../cli/" class="md-nav__link">
<span class="md-ellipsis">
CLI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../crud/" class="md-nav__link">
<span class="md-ellipsis">
CRUD
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../db/" class="md-nav__link">
<span class="md-ellipsis">
Database
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../dependencies/" class="md-nav__link">
<span class="md-ellipsis">
Dependencies
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../exceptions/" class="md-nav__link">
<span class="md-ellipsis">
Exceptions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../fixtures/" class="md-nav__link">
<span class="md-ellipsis">
Fixtures
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../logger/" class="md-nav__link">
<span class="md-ellipsis">
Logger
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../metrics/" class="md-nav__link">
<span class="md-ellipsis">
Metrics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pytest/" class="md-nav__link">
<span class="md-ellipsis">
Pytest
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../schemas/" class="md-nav__link">
<span class="md-ellipsis">
Schemas
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Security
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="././" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Security
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Overview
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#auth-sources" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Auth sources
</span>
</span>
</a>
<nav class="md-nav" aria-label="Auth sources">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#bearertokenauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>BearerTokenAuth</code>
</span>
</span>
</a>
<nav class="md-nav" aria-label="BearerTokenAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#token-prefix" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token prefix
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#token-generation" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token generation
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#cookieauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>CookieAuth</code>
</span>
</span>
</a>
<nav class="md-nav" aria-label="CookieAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#signed-cookies" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Signed cookies
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#apikeyheaderauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>APIKeyHeaderAuth</code>
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#typed-validator-kwargs" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Typed validator kwargs
</span>
</span>
</a>
<nav class="md-nav" aria-label="Typed validator kwargs">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#using-require-inline" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Using <code>.require()</code> inline
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#multiauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
MultiAuth
</span>
</span>
</a>
<nav class="md-nav" aria-label="MultiAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#using-require-on-multiauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Using <code>.require()</code> on MultiAuth
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#prefix-based-dispatch" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Prefix-based dispatch
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#custom-auth-sources" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Custom auth sources
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#oauth-20-oidc-helpers" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
OAuth 2.0 / OIDC helpers
</span>
</span>
</a>
<nav class="md-nav" aria-label="OAuth 2.0 &#x2f; OIDC helpers">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#provider-discovery" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Provider discovery
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#authorization-redirect" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Authorization redirect
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#token-exchange-and-userinfo" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token exchange and userinfo
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#state-encoding" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
State encoding
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" >
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
<span class="md-ellipsis">
Reference
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../reference/cli/" class="md-nav__link">
<span class="md-ellipsis">
CLI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/crud/" class="md-nav__link">
<span class="md-ellipsis">
CRUD
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/db/" class="md-nav__link">
<span class="md-ellipsis">
Database
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/dependencies/" class="md-nav__link">
<span class="md-ellipsis">
Dependencies
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/exceptions/" class="md-nav__link">
<span class="md-ellipsis">
Exceptions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/fixtures/" class="md-nav__link">
<span class="md-ellipsis">
Fixtures
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/logger/" class="md-nav__link">
<span class="md-ellipsis">
Logger
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/metrics/" class="md-nav__link">
<span class="md-ellipsis">
Metrics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/pytest/" class="md-nav__link">
<span class="md-ellipsis">
Pytest
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/schemas/" class="md-nav__link">
<span class="md-ellipsis">
Schemas
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/security/" class="md-nav__link">
<span class="md-ellipsis">
Security
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Examples
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Examples
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../examples/pagination-search/" class="md-nav__link">
<span class="md-ellipsis">
Pagination & Search
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Migration
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Migration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../migration/v4/" class="md-nav__link">
<span class="md-ellipsis">
v4.0
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/v3/" class="md-nav__link">
<span class="md-ellipsis">
v3.0
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/v2/" class="md-nav__link">
<span class="md-ellipsis">
v2.0
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="https://github.com/d3vyce/fastapi-toolsets/releases" class="md-nav__link">
<span class="md-ellipsis">
Changelog ↗
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<div class="md-sidebar-button__wrapper">
<label class="md-sidebar-button" for="__toc"></label>
</div>
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Overview
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#auth-sources" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Auth sources
</span>
</span>
</a>
<nav class="md-nav" aria-label="Auth sources">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#bearertokenauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>BearerTokenAuth</code>
</span>
</span>
</a>
<nav class="md-nav" aria-label="BearerTokenAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#token-prefix" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token prefix
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#token-generation" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token generation
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#cookieauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>CookieAuth</code>
</span>
</span>
</a>
<nav class="md-nav" aria-label="CookieAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#signed-cookies" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Signed cookies
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#apikeyheaderauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
<code>APIKeyHeaderAuth</code>
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#typed-validator-kwargs" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Typed validator kwargs
</span>
</span>
</a>
<nav class="md-nav" aria-label="Typed validator kwargs">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#using-require-inline" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Using <code>.require()</code> inline
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#multiauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
MultiAuth
</span>
</span>
</a>
<nav class="md-nav" aria-label="MultiAuth">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#using-require-on-multiauth" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Using <code>.require()</code> on MultiAuth
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#prefix-based-dispatch" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Prefix-based dispatch
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#custom-auth-sources" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Custom auth sources
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#oauth-20-oidc-helpers" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
OAuth 2.0 / OIDC helpers
</span>
</span>
</a>
<nav class="md-nav" aria-label="OAuth 2.0 &#x2f; OIDC helpers">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#provider-discovery" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Provider discovery
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#authorization-redirect" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Authorization redirect
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#token-exchange-and-userinfo" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
Token exchange and userinfo
</span>
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#state-encoding" class="md-nav__link">
<span class="md-ellipsis">
<span class="md-typeset">
State encoding
</span>
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<nav class="md-path" aria-label="Navigation" >
<ol class="md-path__list">
<li class="md-path__item">
<a href="../.." class="md-path__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-path__item">
<a href="../cli/" class="md-path__link">
<span class="md-ellipsis">
Modules
</span>
</a>
</li>
</ol>
</nav>
<article class="md-content__inner md-typeset">
<a href="https://github.com/d3vyce/fastapi-toolsets/raw/master/docs/module/security.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-file-code-2" viewBox="0 0 24 24"><path d="M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35"/><path d="M14 2v5a1 1 0 0 0 1 1h5M5 16l-3 3 3 3M9 22l3-3-3-3"/></svg>
</a>
<h1 id="security">Security<a class="headerlink" href="#security" title="Permanent link">&para;</a></h1>
<p>Composable authentication helpers for FastAPI that use <code>Security()</code> for OpenAPI documentation and accept user-provided validator functions with full type flexibility.</p>
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>The <code>security</code> module provides four auth source classes, a <code>MultiAuth</code> factory, and a set of OAuth 2.0 / OIDC helper utilities. Each auth class wraps a FastAPI security scheme for OpenAPI and accepts a validator function called as:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="k">await</span> <span class="n">validator</span><span class="p">(</span><span class="n">credential</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></code></pre></div>
<p>where <code>kwargs</code> are the extra keyword arguments provided at instantiation (roles, permissions, enums, etc.). The validator returns the authenticated identity (e.g. a <code>User</code> model) which becomes the route dependency value.</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Security</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">BearerTokenAuth</span>
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">verify_token</span><span class="p">(</span><span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">role</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">User</span><span class="p">:</span>
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">db</span><span class="o">.</span><span class="n">get_by_token</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">user</span> <span class="ow">or</span> <span class="n">user</span><span class="o">.</span><span class="n">role</span> <span class="o">!=</span> <span class="n">role</span><span class="p">:</span>
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> <span class="k">raise</span> <span class="n">UnauthorizedError</span><span class="p">()</span>
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a> <span class="k">return</span> <span class="n">user</span>
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a>
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="n">bearer_admin</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_token</span><span class="p">,</span> <span class="n">role</span><span class="o">=</span><span class="s2">&quot;admin&quot;</span><span class="p">)</span>
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a>
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/admin&quot;</span><span class="p">)</span>
</span><span id="__span-1-13"><a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">admin_route</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">bearer_admin</span><span class="p">)):</span>
</span><span id="__span-1-14"><a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<h2 id="auth-sources">Auth sources<a class="headerlink" href="#auth-sources" title="Permanent link">&para;</a></h2>
<h3 id="bearertokenauth"><a href="../../reference/security/#fastapi_toolsets.security.BearerTokenAuth"><code>BearerTokenAuth</code></a><a class="headerlink" href="#bearertokenauth" title="Permanent link">&para;</a></h3>
<p>Reads the <code>Authorization: Bearer &lt;token&gt;</code> header. Wraps <code>HTTPBearer</code> for OpenAPI.</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">BearerTokenAuth</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="n">bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">verify_token</span><span class="p">)</span>
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a>
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">)</span>
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">me</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">bearer</span><span class="p">)):</span>
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<h4 id="token-prefix">Token prefix<a class="headerlink" href="#token-prefix" title="Permanent link">&para;</a></h4>
<p>The optional <code>prefix</code> parameter restricts a <code>BearerTokenAuth</code> instance to tokens that start with a given string. The prefix is <strong>kept</strong> in the value passed to the validator — store and compare tokens with their prefix included.</p>
<p>This lets you deploy multiple <code>BearerTokenAuth</code> instances in the same application and disambiguate them efficiently in <code>MultiAuth</code>:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="n">user_bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_user</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="s2">&quot;user_&quot;</span><span class="p">)</span> <span class="c1"># matches &quot;Bearer user_...&quot;</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="n">org_bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_org</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="s2">&quot;org_&quot;</span><span class="p">)</span> <span class="c1"># matches &quot;Bearer org_...&quot;</span>
</span></code></pre></div>
<p>Use <a href="#token-generation"><code>generate_token()</code></a> to create correctly-prefixed tokens.</p>
<h4 id="token-generation">Token generation<a class="headerlink" href="#token-generation" title="Permanent link">&para;</a></h4>
<p><code>BearerTokenAuth.generate_token()</code> produces a secure random token ready to store in your database and return to the client. If a prefix is configured it is prepended automatically:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="n">bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_token</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="s2">&quot;user_&quot;</span><span class="p">)</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="n">token</span> <span class="o">=</span> <span class="n">bearer</span><span class="o">.</span><span class="n">generate_token</span><span class="p">()</span> <span class="c1"># e.g. &quot;user_Xk3mN...&quot;</span>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="k">await</span> <span class="n">db</span><span class="o">.</span><span class="n">store_token</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="k">return</span> <span class="p">{</span><span class="s2">&quot;access_token&quot;</span><span class="p">:</span> <span class="n">token</span><span class="p">,</span> <span class="s2">&quot;token_type&quot;</span><span class="p">:</span> <span class="s2">&quot;bearer&quot;</span><span class="p">}</span>
</span></code></pre></div>
<p>The client sends <code>Authorization: Bearer user_Xk3mN...</code> and the validator receives the full token (prefix included) to compare against the stored value.</p>
<h3 id="cookieauth"><a href="../../reference/security/#fastapi_toolsets.security.CookieAuth"><code>CookieAuth</code></a><a class="headerlink" href="#cookieauth" title="Permanent link">&para;</a></h3>
<p>Reads a named cookie. Wraps <code>APIKeyCookie</code> for OpenAPI.</p>
<p>Cookies are issued with the <code>Secure</code> flag set by default, meaning they are only transmitted over HTTPS. Set <code>secure=False</code> when running locally over plain HTTP:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">CookieAuth</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="c1"># Production (HTTPS) — default</span>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="n">cookie_auth</span> <span class="o">=</span> <span class="n">CookieAuth</span><span class="p">(</span><span class="s2">&quot;session&quot;</span><span class="p">,</span> <span class="n">validator</span><span class="o">=</span><span class="n">verify_session</span><span class="p">)</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="c1"># Local development (HTTP only)</span>
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="n">cookie_auth</span> <span class="o">=</span> <span class="n">CookieAuth</span><span class="p">(</span><span class="s2">&quot;session&quot;</span><span class="p">,</span> <span class="n">validator</span><span class="o">=</span><span class="n">verify_session</span><span class="p">,</span> <span class="n">secure</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a>
</span><span id="__span-5-9"><a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">)</span>
</span><span id="__span-5-10"><a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">me</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">cookie_auth</span><span class="p">)):</span>
</span><span id="__span-5-11"><a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<h4 id="signed-cookies">Signed cookies<a class="headerlink" href="#signed-cookies" title="Permanent link">&para;</a></h4>
<p>Pass <code>secret_key</code> to enable HMAC-SHA256 signed, tamper-proof cookies. The cookie payload includes an expiry timestamp (<code>ttl</code>, default 24 h). No database entry is required — the signature is self-contained.</p>
<p>Use <code>set_cookie()</code> to issue the signed cookie on login and <code>delete_cookie()</code> to clear it on logout:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1"># Production</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="n">cookie_auth</span> <span class="o">=</span> <span class="n">CookieAuth</span><span class="p">(</span><span class="s2">&quot;session&quot;</span><span class="p">,</span> <span class="n">verify_session</span><span class="p">,</span> <span class="n">secret_key</span><span class="o">=</span><span class="s2">&quot;your-secret&quot;</span><span class="p">)</span>
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a>
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a><span class="c1"># Local development</span>
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="n">cookie_auth</span> <span class="o">=</span> <span class="n">CookieAuth</span><span class="p">(</span><span class="s2">&quot;session&quot;</span><span class="p">,</span> <span class="n">verify_session</span><span class="p">,</span> <span class="n">secret_key</span><span class="o">=</span><span class="s2">&quot;your-secret&quot;</span><span class="p">,</span> <span class="n">secure</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a>
</span><span id="__span-6-7"><a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/login&quot;</span><span class="p">)</span>
</span><span id="__span-6-8"><a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">login</span><span class="p">(</span><span class="n">response</span><span class="p">:</span> <span class="n">Response</span><span class="p">):</span>
</span><span id="__span-6-9"><a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a> <span class="n">cookie_auth</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
</span><span id="__span-6-10"><a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;ok&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
</span><span id="__span-6-11"><a id="__codelineno-6-11" name="__codelineno-6-11" href="#__codelineno-6-11"></a>
</span><span id="__span-6-12"><a id="__codelineno-6-12" name="__codelineno-6-12" href="#__codelineno-6-12"></a><span class="nd">@app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/logout&quot;</span><span class="p">)</span>
</span><span id="__span-6-13"><a id="__codelineno-6-13" name="__codelineno-6-13" href="#__codelineno-6-13"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">logout</span><span class="p">(</span><span class="n">response</span><span class="p">:</span> <span class="n">Response</span><span class="p">):</span>
</span><span id="__span-6-14"><a id="__codelineno-6-14" name="__codelineno-6-14" href="#__codelineno-6-14"></a> <span class="n">cookie_auth</span><span class="o">.</span><span class="n">delete_cookie</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
</span><span id="__span-6-15"><a id="__codelineno-6-15" name="__codelineno-6-15" href="#__codelineno-6-15"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;ok&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
</span><span id="__span-6-16"><a id="__codelineno-6-16" name="__codelineno-6-16" href="#__codelineno-6-16"></a>
</span><span id="__span-6-17"><a id="__codelineno-6-17" name="__codelineno-6-17" href="#__codelineno-6-17"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">)</span>
</span><span id="__span-6-18"><a id="__codelineno-6-18" name="__codelineno-6-18" href="#__codelineno-6-18"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">me</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">cookie_auth</span><span class="p">)):</span>
</span><span id="__span-6-19"><a id="__codelineno-6-19" name="__codelineno-6-19" href="#__codelineno-6-19"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<p>When <code>secret_key</code> is not set, the raw cookie value is passed directly to the validator (stateful session behaviour — you manage the session store).</p>
<h3 id="apikeyheaderauth"><a href="../../reference/security/#fastapi_toolsets.security.APIKeyHeaderAuth"><code>APIKeyHeaderAuth</code></a><a class="headerlink" href="#apikeyheaderauth" title="Permanent link">&para;</a></h3>
<p>Reads an API key from a named HTTP header. Wraps <code>APIKeyHeader</code> for OpenAPI.</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIKeyHeaderAuth</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="n">api_key_auth</span> <span class="o">=</span> <span class="n">APIKeyHeaderAuth</span><span class="p">(</span><span class="s2">&quot;X-API-Key&quot;</span><span class="p">,</span> <span class="n">validator</span><span class="o">=</span><span class="n">verify_api_key</span><span class="p">)</span>
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a>
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/data&quot;</span><span class="p">)</span>
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">data</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">api_key_auth</span><span class="p">)):</span>
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<p>The header name is configurable — use any header your API defines (e.g. <code>"X-API-Key"</code>, <code>"Authorization"</code>, <code>"X-Service-Token"</code>).</p>
<h2 id="typed-validator-kwargs">Typed validator kwargs<a class="headerlink" href="#typed-validator-kwargs" title="Permanent link">&para;</a></h2>
<p>All auth classes forward extra instantiation keyword arguments to the validator. Arguments can be any type — enums, strings, integers, etc. The validator returns the authenticated identity, which FastAPI injects directly into the route handler.</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">verify_token</span><span class="p">(</span><span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">role</span><span class="p">:</span> <span class="n">Role</span><span class="p">,</span> <span class="n">permission</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">User</span><span class="p">:</span>
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">decode_token</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a> <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">role</span> <span class="o">!=</span> <span class="n">role</span> <span class="ow">or</span> <span class="n">permission</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">user</span><span class="o">.</span><span class="n">permissions</span><span class="p">:</span>
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a> <span class="k">raise</span> <span class="n">UnauthorizedError</span><span class="p">()</span>
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a> <span class="k">return</span> <span class="n">user</span>
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a>
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="n">bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_token</span><span class="p">,</span> <span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">,</span> <span class="n">permission</span><span class="o">=</span><span class="s2">&quot;billing:read&quot;</span><span class="p">)</span>
</span></code></pre></div>
<p>Each auth instance is self-contained — create a separate instance per distinct requirement instead of passing requirements through <code>Security(scopes=[...])</code>.</p>
<h3 id="using-require-inline">Using <code>.require()</code> inline<a class="headerlink" href="#using-require-inline" title="Permanent link">&para;</a></h3>
<p>If declaring a new top-level variable per role feels verbose, use <code>.require()</code> to create a configured clone directly in the route decorator. The original instance is not mutated:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="n">bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_token</span><span class="p">)</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/admin/stats&quot;</span><span class="p">)</span>
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">admin_stats</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">bearer</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">))):</span>
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&quot;Hello admin </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">}</span>
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a>
</span><span id="__span-9-7"><a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/profile&quot;</span><span class="p">)</span>
</span><span id="__span-9-8"><a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">profile</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">bearer</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">USER</span><span class="p">))):</span>
</span><span id="__span-9-9"><a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="p">}</span>
</span></code></pre></div>
<p><code>.require()</code> kwargs are merged over existing ones — new values win on conflict.
The <code>prefix</code> (for <code>BearerTokenAuth</code>), cookie name and <code>secret_key</code> (for
<code>CookieAuth</code>), and header name (for <code>APIKeyHeaderAuth</code>) are always preserved.</p>
<h2 id="multiauth">MultiAuth<a class="headerlink" href="#multiauth" title="Permanent link">&para;</a></h2>
<p><a href="../../reference/security/#fastapi_toolsets.security.MultiAuth"><code>MultiAuth</code></a> combines multiple auth sources into a single callable. Sources are tried in order; the first one that finds a credential wins.</p>
<p>If a credential is extracted but the validator raises, the exception propagates immediately — the remaining sources are <strong>not</strong> tried. This prevents silent fallthrough on invalid credentials.</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">MultiAuth</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="n">multi</span> <span class="o">=</span> <span class="n">MultiAuth</span><span class="p">(</span><span class="n">user_bearer</span><span class="p">,</span> <span class="n">org_bearer</span><span class="p">,</span> <span class="n">cookie_auth</span><span class="p">)</span>
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/data&quot;</span><span class="p">)</span>
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">data_route</span><span class="p">(</span><span class="n">user</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">multi</span><span class="p">)):</span>
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<h3 id="using-require-on-multiauth">Using <code>.require()</code> on MultiAuth<a class="headerlink" href="#using-require-on-multiauth" title="Permanent link">&para;</a></h3>
<p><code>MultiAuth</code> also supports <code>.require()</code>, which propagates the kwargs to every source that implements it. Sources that do not (e.g. custom <code>AuthSource</code> subclasses) are passed through unchanged:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="n">multi</span> <span class="o">=</span> <span class="n">MultiAuth</span><span class="p">(</span><span class="n">bearer</span><span class="p">,</span> <span class="n">cookie</span><span class="p">)</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/admin&quot;</span><span class="p">)</span>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">admin</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span> <span class="o">=</span> <span class="n">Security</span><span class="p">(</span><span class="n">multi</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">))):</span>
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a> <span class="k">return</span> <span class="n">user</span>
</span></code></pre></div>
<p>This is equivalent to calling <code>.require()</code> on each source individually:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="c1"># These two are identical</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="n">multi</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">)</span>
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a>
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="n">MultiAuth</span><span class="p">(</span>
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a> <span class="n">bearer</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">),</span>
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a> <span class="n">cookie</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="n">role</span><span class="o">=</span><span class="n">Role</span><span class="o">.</span><span class="n">ADMIN</span><span class="p">),</span>
</span><span id="__span-12-7"><a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="p">)</span>
</span></code></pre></div>
<h3 id="prefix-based-dispatch">Prefix-based dispatch<a class="headerlink" href="#prefix-based-dispatch" title="Permanent link">&para;</a></h3>
<p>Because <code>extract()</code> is pure string matching (no I/O), prefix-based source selection is essentially free. Only the matching source's validator (which may involve DB or network I/O) is ever called:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="n">user_bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_user</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="s2">&quot;user_&quot;</span><span class="p">)</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="n">org_bearer</span> <span class="o">=</span> <span class="n">BearerTokenAuth</span><span class="p">(</span><span class="n">verify_org</span><span class="p">,</span> <span class="n">prefix</span><span class="o">=</span><span class="s2">&quot;org_&quot;</span><span class="p">)</span>
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="n">multi</span> <span class="o">=</span> <span class="n">MultiAuth</span><span class="p">(</span><span class="n">user_bearer</span><span class="p">,</span> <span class="n">org_bearer</span><span class="p">)</span>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a>
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="c1"># &quot;Bearer user_alice&quot; → only verify_user runs, receives &quot;user_alice&quot;</span>
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a><span class="c1"># &quot;Bearer org_acme&quot; → only verify_org runs, receives &quot;org_acme&quot;</span>
</span></code></pre></div>
<p>Tokens are stored and compared <strong>with their prefix</strong> — use <code>generate_token()</code> on each source to issue correctly-prefixed tokens:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="n">user_token</span> <span class="o">=</span> <span class="n">user_bearer</span><span class="o">.</span><span class="n">generate_token</span><span class="p">()</span> <span class="c1"># &quot;user_...&quot;</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="n">org_token</span> <span class="o">=</span> <span class="n">org_bearer</span><span class="o">.</span><span class="n">generate_token</span><span class="p">()</span> <span class="c1"># &quot;org_...&quot;</span>
</span></code></pre></div>
<h2 id="custom-auth-sources">Custom auth sources<a class="headerlink" href="#custom-auth-sources" title="Permanent link">&para;</a></h2>
<p>Subclass <a href="../../reference/security/#fastapi_toolsets.security.AuthSource"><code>AuthSource</code></a> to implement any credential extraction strategy. You only need to implement <code>extract()</code> and <code>authenticate()</code>:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">AuthSource</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.exceptions</span><span class="w"> </span><span class="kn">import</span> <span class="n">UnauthorizedError</span>
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="k">class</span><span class="w"> </span><span class="nc">MTLSAuth</span><span class="p">(</span><span class="n">AuthSource</span><span class="p">):</span>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">extract</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a> <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;X-Client-Cert-DN&quot;</span><span class="p">)</span> <span class="ow">or</span> <span class="kc">None</span>
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a>
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a> <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">authenticate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">credential</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a> <span class="n">dn</span> <span class="o">=</span> <span class="n">parse_dn</span><span class="p">(</span><span class="n">credential</span><span class="p">)</span>
</span><span id="__span-15-10"><a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a> <span class="k">if</span> <span class="n">dn</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;O&quot;</span><span class="p">)</span> <span class="o">!=</span> <span class="s2">&quot;MyOrg&quot;</span><span class="p">:</span>
</span><span id="__span-15-11"><a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a> <span class="k">raise</span> <span class="n">UnauthorizedError</span><span class="p">()</span>
</span><span id="__span-15-12"><a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a> <span class="k">return</span> <span class="p">{</span><span class="s2">&quot;dn&quot;</span><span class="p">:</span> <span class="n">credential</span><span class="p">}</span>
</span></code></pre></div>
<p>Custom sources work transparently inside <code>MultiAuth</code>.</p>
<h2 id="oauth-20-oidc-helpers">OAuth 2.0 / OIDC helpers<a class="headerlink" href="#oauth-20-oidc-helpers" title="Permanent link">&para;</a></h2>
<p>The module provides standalone async utilities for building OAuth 2.0 / OIDC login flows. They handle provider discovery, authorization redirects, token exchange, and state encoding — leaving JWT validation and session management to your application.</p>
<h3 id="provider-discovery">Provider discovery<a class="headerlink" href="#provider-discovery" title="Permanent link">&para;</a></h3>
<p><a href="../../reference/security/#fastapi_toolsets.security.oauth_resolve_provider_urls"><code>oauth_resolve_provider_urls()</code></a> fetches the OIDC discovery document and returns the endpoint URLs. Results are cached in-process to avoid repeated network calls:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">oauth_resolve_provider_urls</span>
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a>
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a><span class="n">auth_url</span><span class="p">,</span> <span class="n">token_url</span><span class="p">,</span> <span class="n">userinfo_url</span> <span class="o">=</span> <span class="k">await</span> <span class="n">oauth_resolve_provider_urls</span><span class="p">(</span>
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a> <span class="s2">&quot;https://accounts.google.com/.well-known/openid-configuration&quot;</span>
</span><span id="__span-16-5"><a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a><span class="p">)</span>
</span></code></pre></div>
<p>Returns a <code>(authorization_url, token_url, userinfo_url)</code> tuple. <code>userinfo_url</code> is <code>None</code> when the provider does not advertise one.</p>
<h3 id="authorization-redirect">Authorization redirect<a class="headerlink" href="#authorization-redirect" title="Permanent link">&para;</a></h3>
<p><a href="../../reference/security/#fastapi_toolsets.security.oauth_build_authorization_redirect"><code>oauth_build_authorization_redirect()</code></a> constructs the redirect to the provider's authorization page. It requires a <code>state_token</code> — a random CSRF token generated by <a href="../../reference/security/#fastapi_toolsets.security.oauth_generate_state_token"><code>oauth_generate_state_token()</code></a> — that must be stored server-side (e.g. in the session) and verified on the callback to prevent login-CSRF attacks (<a href="https://datatracker.ietf.org/doc/html/rfc6749#section-10.12">RFC 6749 §10.12</a>):</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">Request</span>
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">oauth_build_authorization_redirect</span><span class="p">,</span> <span class="n">oauth_generate_state_token</span>
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a>
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/auth/google/login&quot;</span><span class="p">)</span>
</span><span id="__span-17-5"><a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">google_login</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">):</span>
</span><span id="__span-17-6"><a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a> <span class="n">auth_url</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="k">await</span> <span class="n">oauth_resolve_provider_urls</span><span class="p">(</span><span class="n">GOOGLE_DISCOVERY_URL</span><span class="p">)</span>
</span><span id="__span-17-7"><a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a> <span class="n">state_token</span> <span class="o">=</span> <span class="n">oauth_generate_state_token</span><span class="p">()</span>
</span><span id="__span-17-8"><a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a> <span class="n">request</span><span class="o">.</span><span class="n">session</span><span class="p">[</span><span class="s2">&quot;oauth_state&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">state_token</span> <span class="c1"># requires SessionMiddleware</span>
</span><span id="__span-17-9"><a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a> <span class="k">return</span> <span class="n">oauth_build_authorization_redirect</span><span class="p">(</span>
</span><span id="__span-17-10"><a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a> <span class="n">auth_url</span><span class="p">,</span>
</span><span id="__span-17-11"><a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a> <span class="n">client_id</span><span class="o">=</span><span class="n">GOOGLE_CLIENT_ID</span><span class="p">,</span>
</span><span id="__span-17-12"><a id="__codelineno-17-12" name="__codelineno-17-12" href="#__codelineno-17-12"></a> <span class="n">scopes</span><span class="o">=</span><span class="s2">&quot;openid email profile&quot;</span><span class="p">,</span>
</span><span id="__span-17-13"><a id="__codelineno-17-13" name="__codelineno-17-13" href="#__codelineno-17-13"></a> <span class="n">redirect_uri</span><span class="o">=</span><span class="s2">&quot;https://myapp.com/auth/google/callback&quot;</span><span class="p">,</span>
</span><span id="__span-17-14"><a id="__codelineno-17-14" name="__codelineno-17-14" href="#__codelineno-17-14"></a> <span class="n">destination</span><span class="o">=</span><span class="s2">&quot;/dashboard&quot;</span><span class="p">,</span>
</span><span id="__span-17-15"><a id="__codelineno-17-15" name="__codelineno-17-15" href="#__codelineno-17-15"></a> <span class="n">state_token</span><span class="o">=</span><span class="n">state_token</span><span class="p">,</span>
</span><span id="__span-17-16"><a id="__codelineno-17-16" name="__codelineno-17-16" href="#__codelineno-17-16"></a> <span class="p">)</span>
</span></code></pre></div>
<h3 id="token-exchange-and-userinfo">Token exchange and userinfo<a class="headerlink" href="#token-exchange-and-userinfo" title="Permanent link">&para;</a></h3>
<p><a href="../../reference/security/#fastapi_toolsets.security.oauth_fetch_userinfo"><code>oauth_fetch_userinfo()</code></a> performs the two-step exchange: it POSTs the authorization code to the token endpoint, then GETs the userinfo endpoint with the resulting access token.</p>
<p>On the callback, retrieve the stored token and pass it to <a href="../../reference/security/#fastapi_toolsets.security.oauth_decode_state"><code>oauth_decode_state()</code></a> to verify the CSRF token before processing the code:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Request</span>
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">oauth_decode_state</span><span class="p">,</span> <span class="n">oauth_fetch_userinfo</span>
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a>
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a><span class="nd">@app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/auth/google/callback&quot;</span><span class="p">)</span>
</span><span id="__span-18-5"><a id="__codelineno-18-5" name="__codelineno-18-5" href="#__codelineno-18-5"></a><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">google_callback</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">code</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
</span><span id="__span-18-6"><a id="__codelineno-18-6" name="__codelineno-18-6" href="#__codelineno-18-6"></a> <span class="c1"># Pop token first — single-use, regardless of whether verification succeeds</span>
</span><span id="__span-18-7"><a id="__codelineno-18-7" name="__codelineno-18-7" href="#__codelineno-18-7"></a> <span class="n">state_token</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;oauth_state&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
</span><span id="__span-18-8"><a id="__codelineno-18-8" name="__codelineno-18-8" href="#__codelineno-18-8"></a> <span class="k">if</span> <span class="n">state_token</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span><span id="__span-18-9"><a id="__codelineno-18-9" name="__codelineno-18-9" href="#__codelineno-18-9"></a> <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">&quot;missing OAuth state&quot;</span><span class="p">)</span>
</span><span id="__span-18-10"><a id="__codelineno-18-10" name="__codelineno-18-10" href="#__codelineno-18-10"></a> <span class="n">destination</span> <span class="o">=</span> <span class="n">oauth_decode_state</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">expected_state_token</span><span class="o">=</span><span class="n">state_token</span><span class="p">,</span> <span class="n">fallback</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">)</span>
</span><span id="__span-18-11"><a id="__codelineno-18-11" name="__codelineno-18-11" href="#__codelineno-18-11"></a> <span class="k">if</span> <span class="ow">not</span> <span class="n">destination</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">):</span> <span class="c1"># reject absolute URLs to prevent open-redirect</span>
</span><span id="__span-18-12"><a id="__codelineno-18-12" name="__codelineno-18-12" href="#__codelineno-18-12"></a> <span class="n">destination</span> <span class="o">=</span> <span class="s2">&quot;/&quot;</span>
</span><span id="__span-18-13"><a id="__codelineno-18-13" name="__codelineno-18-13" href="#__codelineno-18-13"></a>
</span><span id="__span-18-14"><a id="__codelineno-18-14" name="__codelineno-18-14" href="#__codelineno-18-14"></a> <span class="n">_</span><span class="p">,</span> <span class="n">token_url</span><span class="p">,</span> <span class="n">userinfo_url</span> <span class="o">=</span> <span class="k">await</span> <span class="n">oauth_resolve_provider_urls</span><span class="p">(</span><span class="n">GOOGLE_DISCOVERY_URL</span><span class="p">)</span>
</span><span id="__span-18-15"><a id="__codelineno-18-15" name="__codelineno-18-15" href="#__codelineno-18-15"></a> <span class="n">userinfo</span> <span class="o">=</span> <span class="k">await</span> <span class="n">oauth_fetch_userinfo</span><span class="p">(</span>
</span><span id="__span-18-16"><a id="__codelineno-18-16" name="__codelineno-18-16" href="#__codelineno-18-16"></a> <span class="n">token_url</span><span class="o">=</span><span class="n">token_url</span><span class="p">,</span>
</span><span id="__span-18-17"><a id="__codelineno-18-17" name="__codelineno-18-17" href="#__codelineno-18-17"></a> <span class="n">userinfo_url</span><span class="o">=</span><span class="n">userinfo_url</span><span class="p">,</span>
</span><span id="__span-18-18"><a id="__codelineno-18-18" name="__codelineno-18-18" href="#__codelineno-18-18"></a> <span class="n">code</span><span class="o">=</span><span class="n">code</span><span class="p">,</span>
</span><span id="__span-18-19"><a id="__codelineno-18-19" name="__codelineno-18-19" href="#__codelineno-18-19"></a> <span class="n">client_id</span><span class="o">=</span><span class="n">GOOGLE_CLIENT_ID</span><span class="p">,</span>
</span><span id="__span-18-20"><a id="__codelineno-18-20" name="__codelineno-18-20" href="#__codelineno-18-20"></a> <span class="n">client_secret</span><span class="o">=</span><span class="n">GOOGLE_CLIENT_SECRET</span><span class="p">,</span>
</span><span id="__span-18-21"><a id="__codelineno-18-21" name="__codelineno-18-21" href="#__codelineno-18-21"></a> <span class="n">redirect_uri</span><span class="o">=</span><span class="s2">&quot;https://myapp.com/auth/google/callback&quot;</span><span class="p">,</span>
</span><span id="__span-18-22"><a id="__codelineno-18-22" name="__codelineno-18-22" href="#__codelineno-18-22"></a> <span class="n">required_scopes</span><span class="o">=</span><span class="s2">&quot;openid email profile&quot;</span><span class="p">,</span>
</span><span id="__span-18-23"><a id="__codelineno-18-23" name="__codelineno-18-23" href="#__codelineno-18-23"></a> <span class="p">)</span>
</span><span id="__span-18-24"><a id="__codelineno-18-24" name="__codelineno-18-24" href="#__codelineno-18-24"></a> <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">db</span><span class="o">.</span><span class="n">upsert_user</span><span class="p">(</span><span class="n">email</span><span class="o">=</span><span class="n">userinfo</span><span class="p">[</span><span class="s2">&quot;email&quot;</span><span class="p">])</span>
</span><span id="__span-18-25"><a id="__codelineno-18-25" name="__codelineno-18-25" href="#__codelineno-18-25"></a> <span class="n">response</span> <span class="o">=</span> <span class="n">RedirectResponse</span><span class="p">(</span><span class="n">destination</span><span class="p">)</span>
</span><span id="__span-18-26"><a id="__codelineno-18-26" name="__codelineno-18-26" href="#__codelineno-18-26"></a> <span class="n">session_cookie</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">))</span>
</span><span id="__span-18-27"><a id="__codelineno-18-27" name="__codelineno-18-27" href="#__codelineno-18-27"></a> <span class="k">return</span> <span class="n">response</span>
</span></code></pre></div>
<p>Pass <code>required_scopes</code> to guard against providers silently granting fewer scopes than requested — <code>oauth_fetch_userinfo</code> raises <code>ValueError</code> if any are missing.</p>
<h3 id="state-encoding">State encoding<a class="headerlink" href="#state-encoding" title="Permanent link">&para;</a></h3>
<p><a href="../../reference/security/#fastapi_toolsets.security.oauth_encode_state"><code>oauth_encode_state()</code></a> and <a href="../../reference/security/#fastapi_toolsets.security.oauth_decode_state"><code>oauth_decode_state()</code></a> encode and decode the destination URL together with the CSRF token embedded in the OAuth <code>state</code> parameter. <code>oauth_decode_state</code> returns <code>fallback</code> if <code>state</code> is absent, malformed, or the token does not match:</p>
<div class="language-python highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi_toolsets.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">oauth_encode_state</span><span class="p">,</span> <span class="n">oauth_decode_state</span>
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a>
</span><span id="__span-19-3"><a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a><span class="n">state_token</span> <span class="o">=</span> <span class="n">oauth_generate_state_token</span><span class="p">()</span>
</span><span id="__span-19-4"><a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a><span class="n">encoded</span> <span class="o">=</span> <span class="n">oauth_encode_state</span><span class="p">(</span><span class="s2">&quot;/dashboard&quot;</span><span class="p">,</span> <span class="n">state_token</span><span class="p">)</span>
</span><span id="__span-19-5"><a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a><span class="n">decoded</span> <span class="o">=</span> <span class="n">oauth_decode_state</span><span class="p">(</span><span class="n">encoded</span><span class="p">,</span> <span class="n">expected_state_token</span><span class="o">=</span><span class="n">state_token</span><span class="p">,</span> <span class="n">fallback</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">)</span> <span class="c1"># &quot;/dashboard&quot;</span>
</span><span id="__span-19-6"><a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a><span class="n">decoded</span> <span class="o">=</span> <span class="n">oauth_decode_state</span><span class="p">(</span><span class="n">encoded</span><span class="p">,</span> <span class="n">expected_state_token</span><span class="o">=</span><span class="s2">&quot;wrong&quot;</span><span class="p">,</span> <span class="n">fallback</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">)</span> <span class="c1"># &quot;/&quot;</span>
</span><span id="__span-19-7"><a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a><span class="n">decoded</span> <span class="o">=</span> <span class="n">oauth_decode_state</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">expected_state_token</span><span class="o">=</span><span class="n">state_token</span><span class="p">,</span> <span class="n">fallback</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">)</span> <span class="c1"># &quot;/&quot;</span>
</span></code></pre></div>
<hr />
<p><a href="../../reference/security/"><span class="twemoji"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7H5a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2m0 4H5V9h2m7-2h-4v10h2v-4h2a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2m0 4h-2V9h2m6 0v6h1v2h-4v-2h1V9h-1V7h4v2Z"/></svg></span> API Reference</a></p>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-circle-arrow-up" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m16 12-4-4-4 4M12 16V8"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../schemas/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Schemas">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-arrow-left" viewBox="0 0 24 24"><path d="m12 19-7-7 7-7M19 12H5"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Schemas
</div>
</div>
</a>
<a href="../../reference/cli/" class="md-footer__link md-footer__link--next" aria-label="Next: CLI">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
CLI
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-arrow-right" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2026 d3vyce
</div>
Made with
<a href="https://zensical.org/" target="_blank" rel="noopener">
Zensical
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate":null,"base":"../..","features":["announce.dismiss","content.action.view","content.code.annotate","content.code.copy","content.code.select","content.footnote.tooltips","content.tabs.link","content.tooltips","navigation.footer","navigation.indexes","navigation.instant","navigation.instant.prefetch","navigation.path","navigation.sections","navigation.tabs","navigation.top","navigation.tracking","search.highlight"],"search":"../../assets/javascripts/workers/search.e2d2d235.min.js","tags":null,"translations":{"clipboard.copied":"Copied to clipboard","clipboard.copy":"Copy to clipboard","search.result.more.one":"1 more on this page","search.result.more.other":"# more on this page","search.result.none":"No matching documents","search.result.one":"1 matching document","search.result.other":"# matching documents","search.result.placeholder":"Type to start searching","search.result.term.missing":"Missing","select.version":"Select version"},"version":{"alias":true,"default":"stable","provider":"mike"}}</script>
<script src="../../assets/javascripts/bundle.dbc0afdc.min.js"></script>
</body>
</html>