class

FastAPIAuthTagger

Inherits FrameworkTagger < FileHelper < Tagger < Reference < Object

Constants

ALIAS_ANNOTATION = /:\s*([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)?)\s*(?=[,)=]|$)/m
ANNOTATED_ALIAS = /^\s*(\w+)\s*(?::\s*[\w.\[\]]+\s*)?=\s*Annotated\s*\[(.+)/

CurrentUser = Annotated[User, Depends(get_current_user)], the alias the FastAPI docs and full-stack template annotate handler parameters with.

ANNOTATED_ALIAS_DEPENDENCY = /^\s*(\w+)\s*(?::\s*[\w.\[\]]+\s*)?=\s*Annotated\s*\[.*\bDepends\s*\(\s*([\w.]+)/
AUTH_CHECKER_SUFFIX = ["checker", "check", "required", "guard", "verifier", "validator"] of ::String
AUTH_PARAM_PATTERNS = [{/:\s*User\s*=\s*Depends/, "FastAPI User dependency injection"}, {/:\s*TokenData\s*=\s*Depends/, "FastAPI TokenData dependency"}, {/:\s*HTTPAuthorizationCredentials\s*=\s*Security/, "FastAPI HTTP authorization"}]

Auth-related parameter type annotations

AUTHZ_FIELD = /\.(?:is_superuser|is_admin|is_staff|owner_id|roles?|permissions?|scopes)\b/

Authorization, as opposed to authentication: a role / ownership field being read, or the handler answering 403. A dependency counts only on the field — get_current_user raises 403 for a bad token, which is authentication — while a route body counts on either. ponytail: .permissions / .roles may be a data read rather than a check; a per-statement if parse would separate them.

BARE_RETURN = /^\s*return(?:\s+None)?\s*$/
BODY_401_CHECK = "FastAPI handler 401 check"
BODY_IF = /^(el)?if\s+(.+?):\s*(raise\b.*|return\b.*)?$/
CALLER_ALIAS_PARAM = /(\w+)\s*:\s*(\w+)\s*[,)=]/
CALLER_ANNOTATED_PARAM = /(\w+)\s*:\s*Annotated\s*\[[^\n]*?\b(Depends|Security)\s*\(\s*([\w.]*)/
CALLER_PARAM = /(\w+)\s*(?::[^=,()]*(?:\[[^\]]*\][^=,()]*)?)?=\s*(Depends|Security)\s*\(\s*([\w.]*)/
CALLER_REFERENCE = /(?<![\w.])(?:request\.(?:state\.)?user\b|self\.[A-Za-z_]\w*|[A-Za-z_]\w*)/
CALLER_TERMS = /\brequest\b|\b(?:token|credentials?|authorization|subject|principal|user|anonymous|session|api_?key|headers?|cookies?)\b|is_anonymous|Anonymous/i
CALLER_WORDS = ["user", "account", "principal", "identity", "viewer", "actor", "caller"] of ::String

Name components of a dependency that hands the route its caller.

CLASS_DEF = /^(\s*)class\s+(\w+)\s*(?:\(([^)]*)\))?\s*:/
CLASS_DEPENDENCY_ATTR = /^\s*(\w+)\s*(?::[^=]+)?=\s*(?:Depends|Security)\s*\(/
DEPENDENCIES_LIST = /\bdependencies\s*=\s*\[([^\]]*)\]/m
DEPENDENCY_CALL = /\b(Depends|Security)\s*\(\s*([\w.]+)/

Any other Depends(x) / Security(x) whose callable is named for auth: get_current_active_superuser, reusable_oauth2, verify_api_key, ...

Matched on the name's snake/camel components, never as a substring: get_author_by_id is not auth, and get_token_count / get_roles_repo / permission_service fetch data rather than guard the route. The weak words (token, role, permission) count only as the last component or before a checker word (verify_token, check_permissions, RoleChecker).

DEPENDS_AUTH_PATTERNS = [{/Depends\s*\(\s*get_current_user\b/, "FastAPI Depends(get_current_user)"}, {/Depends\s*\(\s*get_current_active_user\b/, "FastAPI Depends(get_current_active_user)"}, {/Depends\s*\(\s*oauth2_scheme\b/, "FastAPI OAuth2 dependency"}, {/Depends\s*\(\s*get_token\b/, "FastAPI token dependency"}, {/Depends\s*\(\s*verify_token\b/, "FastAPI token verification"}, {/Depends\s*\(\s*auth\b/, "FastAPI auth dependency"}, {/Depends\s*\(\s*require_auth\b/, "FastAPI require_auth dependency"}, {/Depends\s*\(\s*check_permission\b/, "FastAPI permission check"}, {/Depends\s*\(\s*RoleChecker/, "FastAPI role checker"}]

Depends() with auth-related callables

FACTORY_ITEM = /\A([A-Za-z_][\w.]*)\s*\(/
FORBIDDEN = /status_code\s*=\s*(?:403\b|status\.HTTP_403_FORBIDDEN)|\bHTTP_403_FORBIDDEN\b|HTTPException\s*\(\s*403\b/
IMPORT_FROM = /^\s*from\s+[\w.]+\s+import\s+(\([^)]*\)|[^\n]+)/m
INCLUDE_ROUTER_CALL = /^\s*([\w.]+?)\s*\.\s*include_router\s*\(/

self.app.include_router(...) is indexed too: its parent never resolves, so that mount counts as one without a dependency.

INSTANCE_CALL = /=\s*([A-Za-z_][\w.]*)\s*\(/
LITERAL = /\A(?:True|False|None|-?\d[\d_.]*|[rbuf]?(?:"[^"]*"|'[^']*'))\z/
NAME_COMPONENT = /[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/
NONE_SKIPPING_TEST = /^\(?\s*(request\.(?:state\.)?user|(?:self\.)?[A-Za-z_]\w*)(?:\s+is\s+not\s+None)?\s*(?:\band\b|\)?\s*$)/
PYTHON_DEF = /^(?:async\s+)?def\s+(\w+)\s*\(/
REFUSAL_MARKER = /\b(?:401|403)\b|\bHTTP_40[13]_|Unauthori[sz]ed|Forbidden|NotAuthenticated|PermissionDenied|AuthenticationFailed/
RETURN_ANNOTATION = /\)\s*->\s*([^\n]+?)\s*:\s*$/m
RETURNED_DEPENDS = /^\s*return\s+(?:Depends|Security)\s*\(\s*(?:dependency\s*=\s*)?(\w+)\s*[,)]/
ROUTE_DECORATOR = /^\s*@(\w+)\.(?:get|post|put|patch|delete|options|head|trace|api_route|websocket)\b/
SECURITY_PATTERNS = [{/Security\s*\(\s*oauth2_scheme/, "FastAPI Security(oauth2_scheme)"}, {/Security\s*\(\s*api_key/, "FastAPI Security(api_key)"}, {/Security\s*\(\s*http_bearer/, "FastAPI Security(http_bearer)"}, {/Security\s*\(\s*http_basic/, "FastAPI Security(http_basic)"}]

Security() declarations

SETTING_REF = /\bself\.[A-Za-z_]\w*\b(?!\s*\()/
SPLIT_RULES = Noir::TopLevelSplit::Rules.new
STRONG_AUTH_WORDS = ["auth", "authn", "authz", "authenticate", "authenticated", "authentication", "authorize", "authorized", "authorization", "oauth", "jwt", "bearer", "superuser", "login", "apikey"] of ::String
UNAUTHENTICATED_MARKER = /\b401\b|\bHTTP_401_|Unauthori[sz]ed|NotAuthenticated|AuthenticationFailed/
UNAUTHORIZED_STATUS = /status_code\s*=\s*(?:401\b|status\.HTTP_401_UNAUTHORIZED)/
UNBOUND = "\u0000"
WEAK_AUTH_WORDS = ["token", "tokens", "role", "roles", "permission", "permissions", "scope", "scopes"] of ::String

Constructors

new(options : Hash(String, YAML::Any))
Source

Class methods

target_techs
Source

Instance methods

perform(endpoints : Array(Endpoint)) : Array(Endpoint)

The per-endpoint shape: look at each endpoint, tag in place, hand the array back. Taggers declare only check_endpoint; those that need a pass over the whole set first (a project pre-scan, mcp's legacy prefixes) override perform and call super or loop themselves.

Source

Nested types