Skip to content

Reference

armasec.armasec

This module defines the core Armasec class.

Armasec

This is a factory class for TokenSecurity. It allows the machinery of armasec to be initialized correctly so that the factory method lockdown can initialize new instances of TokenSecurity to protect routes. It's not essential to use Armasec to secure routes, but it cuts down on the boilerplate necessary to do so.

__init__
__init__(domain_configs: Optional[List[DomainConfig]] = None, debug_logger: Optional[Callable[[str], None]] = noop, debug_exceptions: bool = False, **kargs)

Stores initialization values for the TokenSecurity. All are passed through.

Parameters:

Name Type Description Default
domain_configs Optional[List[DomainConfig]]

List of domain configuration to authenticate the tokens against.

None
debug_logger Optional[Callable[[str], None]]

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug

noop
debug_exceptions bool

If True, raise original exceptions. Should only be used in a testing or debugging context.

False
kargs

Arguments compatible to instantiate the DomainConfig model.

{}
lockdown cached
lockdown(*scopes: str, permission_mode: PermissionMode = PermissionMode.ALL, skip_plugins: bool = False) -> TokenSecurity

Initialize an instance of TokenSecurity to lockdown a route. Uses memoization to minimize the number of TokenSecurity instances initialized. Applies supplied permission_mode when checking token permssions against TokenSecurity scopes.

Parameters:

Name Type Description Default
scopes str

A list of scopes needed to access the endpoint.

()
permissions_mode

If "ALL", all scopes listed are required for access. If "SOME", only one of the scopes listed are required for access.

required
skip_plugins bool

If True, do not evaluate plugin validators.

False
lockdown_all
lockdown_all(*scopes: str, skip_plugins: bool = False) -> TokenSecurity

Initialize an instance of TokenSecurity to lockdown a route. Uses memoization to minimize the number of TokenSecurity instances initialized. Requires all the scopes in the TokenSecurity instance to be included in the token permissions. This is just a wrapper around lockdown() with default permission_mode and is only included for symmetry.

Parameters:

Name Type Description Default
scopes str

A list of the scopes needed to access the endpoint. All are required.

()
skip_plugins bool

If True, do not evaluate plugin validators.

False
lockdown_some
lockdown_some(*scopes: str, skip_plugins: bool = False) -> TokenSecurity

Initialize an instance of TokenSecurity to lockdown a route. Uses memoization to minimize the number of TokenSecurity instances initialized. Requires at least one permission in the token to match a scope attached to the TokenSecurity instance.

Parameters:

Name Type Description Default
scopes str

A list of the scopes needed to access the endpoint. Only one is required.

()
skip_plugins bool

If True, do not evaluate plugin validators.

False

armasec.exceptions

ArmasecError

Bases: Buzz

A custom exception class used for checking conditions and handling other exceptions.

Attributes:

Name Type Description
status_code int

The HTTP status code indicated by the error. Set to 400.

AuthenticationError

Bases: ArmasecError

Indicates a failure to authenticate and decode jwt.

Attributes:

Name Type Description
status_code int

The HTTP status code indicated by the error. Set to 401.

AuthorizationError

Bases: ArmasecError

Indicates that the provided claims don't match the claims required for a protected endpoint.

Attributes:

Name Type Description
status_code int

The HTTP status code indicated by the error. Set to 403.

PayloadMappingError

Bases: ArmasecError

Indicates that the configured payload_claim_mapping did not match a path in the token.

Attributes:

Name Type Description
status_code int

The HTTP status code indicated by the error. Set to 500.

armasec.openid_config_loader

This module provides the OpenidConfigLoader which is used to load openid-configuration data from an OIDC provider.

OpenidConfigLoader

config property
config: OpenidConfig

Retrive the openid config from an OIDC provider. Lazy loads the config so that API calls are deferred until the coniguration is needed.

jwks property
jwks: JWKs

Retrives JWKs public keys from an OIDC provider. Lazy loads the jwks so that API calls are deferred until the jwks are needed.

__init__
__init__(domain: str, use_https: bool = True, debug_logger: Optional[Callable[..., None]] = None)

Initializes a base TokenManager.

Parameters:

Name Type Description Default
secret

The secret key needed to decode a token

required
domain str

The domain of the OIDC provider. This is to construct the openid-configuration url

required
use_https bool

If falsey, use http instead of https (the default).

True
debug_logger Optional[Callable[..., None]]

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug

None
build_openid_config_url staticmethod
build_openid_config_url(domain: str, use_https: bool = True)

Builds a url for an openid configuration given a domain.

Parameters:

Name Type Description Default
domain str

The domain of the OIDC provider for which to build a URL

required
use_https bool

Use https for the URL by default. If falsey, use http instead.

True

armasec.pytest_extension

This module provides a pytest plugin for testing.

build_mock_openid_server

build_mock_openid_server(domain, openid_config, jwk, jwks_uri) -> Callable[[str, OpenidConfig, JWK, str], _GeneratorContextManager]

Provide a fixture that returns a context manager that mocks opend-config routes.

Parameters:

Name Type Description Default
domain

The domain of the openid server to mock.

required
openid_config

The config to return from the mocked config route.

required
jwk

The jwk to return from the mocked jwk route.

required
jwks_uri

The URL of the jwks route to mock.

required

Returns:

Type Description
Callable[[str, OpenidConfig, JWK, str], _GeneratorContextManager]

A context manager that, while active, mocks the openid routes needed by Armasec.

build_rs256_token

build_rs256_token(rs256_private_key, rs256_iss, rs256_sub, rs256_kid)

Provide a fixture that returns a helper method that can build a JWT.

The JWT is signed with the private key provided by the rs256_private_key.

Parameters:

Name Type Description Default
rs256_private_key

An implicit fixture parameter.

required
rs256_iss

An implicit fixture parameter.

required
rs256_sub

An implicit fixture parameter.

required

mock_openid_server

mock_openid_server(rs256_domain, rs256_openid_config, rs256_jwk, rs256_jwks_uri)

Provide a fixture that mocks an openid server using the extension fixtures.

Parameters:

Name Type Description Default
rs256_domain

An implicit fixture parameter.

required
rs256_openid_config

An implicit fixture parameter.

required
rs256_jwk

An implicit fixture parameter.

required
rs256_jwks_uri

An implicit fixture parameter.

required

rs256_domain

rs256_domain()

Provide a fixture that returns a domain for use in other fixtures.

The value here doesn't really have anything to do with an actual domain name.

rs256_domain_config

rs256_domain_config(rs256_domain)

Provide a fixture that returns the DomainConfig model for the default rs256 domain.

Parameters:

Name Type Description Default
rs256_domain

An implicit fixture parameter.

required

rs256_iss

rs256_iss(rs256_domain)

Provide a fixture that returns an issuer claim for use in other fixtures.

Parameters:

Name Type Description Default
rs256_domain

An implicit fixture parameter.

required

rs256_jwk

rs256_jwk(rs256_kid)

Provide a fixture that returns a JWK for use in other fixtures.

Parameters:

Name Type Description Default
rs256_kid

An implicit fixture parameter.

required

rs256_jwks_uri

rs256_jwks_uri(rs256_domain)

Provide a fixture that returns a jwks uri for use in other fixtures.

Parameters:

Name Type Description Default
rs256_jwks_uri

An implicit fixture parameter.

required

rs256_kid

rs256_kid()

Provide a fixture that returns a KID header value for use in other fixtures.

rs256_openid_config

rs256_openid_config(rs256_iss, rs256_jwks_uri)

Provide a fixture that returns an openid configuration for use in other fixtures.

Parameters:

Name Type Description Default
rs256_iss

An implicit fixture parameter.

required
rs256_jwks_uri

An implicit fixture parameter.

required

rs256_private_key

rs256_private_key()

Provide a fixture that returns a pre-generated private key for RS256 hashing in other fixtures.

rs256_public_key

rs256_public_key()

Provide a fixture that returns a pre-generated public key for RS256 hashing in other fixtures.

rs256_sub

rs256_sub()

Provide a fixture that returns a sum claim for use in other fixtures.

armasec.token_decoder

This module provides an abstract base class for algorithmic token decoders

TokenDecoder

Decoder class used to decode tokens given an algorithm and jwks.

__init__
__init__(jwks: JWKs, algorithm: str = 'RS256', debug_logger: Callable[..., None] | None = None, decode_options_override: dict | None = None, permission_extractor: Callable[[dict], list[str]] | None = None)

Initializes a TokenDecoder.

Parameters:

Name Type Description Default
algorithm str

The algorithm to use for decoding. Defaults to RS256.

'RS256'
jwks JWKs

JSON web keys object holding the public keys for decoding.

required
openid_config

The openid_configuration needed for claims such as 'issuer'.

required
debug_logger Callable[..., None] | None

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug

None
decode_options_override dict | None

Options that can override the default behavior of the jwt decode method. For example, one can ignore token expiration by setting this to { "verify_exp": False }

None
permission_extractor Callable[[dict], list[str]] | None

Optional function that may be used to extract permissions from the decoded token dictionary when the permissions are not a top-level claim in the token. If not provided, permissions will be assumed to be a top-level claim in the token.

                 Consider the example token:

                 ```
                 {
                   "exp": 1728627701,
                   "iat": 1728626801,
                   "jti": "24fdb7ef-d773-4e6b-982a-b8126dd58af7",
                   "sub": "dfa64115-40b5-46ab-924c-c376e73f631d",
                   "azp": "my-client",
                   "resource_access": {
                     "my-client": {
                       "roles": [
                         "read:stuff"
                       ]
                     },
                   },
                 }
                 ```

                 In this example, the permissions are found at
                 `resource_access.my-client.roles`. To produce a TokenPayload
                 with the permissions set as expected, you could supply a
                 permission extractor like this:

                 ```
                 def my_extractor(decoded_token: dict) -> list[str]:
                     resource_key = decoded_token["azp"]
                     return decoded_token["resource_access"][resource_key]["roles"]
                 ```
None
decode
decode(token: str, **claims) -> TokenPayload

Decode a JWT into a TokenPayload while checking signatures and claims.

Parameters:

Name Type Description Default
token str

The token to decode.

required
claims

Additional claims to verify in the token.

{}
get_decode_key
get_decode_key(token: str) -> dict

Search for a public keys within the JWKs that matches the incoming token.

Compares the token's unverified header against available JWKs. Uses the matching JWK for the decode key. Raise AuthenticationError if matching public key cannot be found.

Parameters:

Name Type Description Default
token str

The token to match against available JWKs.

required

extract_keycloak_permissions

extract_keycloak_permissions(decoded_token: dict) -> list[str]

Provide a permission extractor for Keycloak.

By default, Keycloak packages the roles for a given client nested within the "resource_access" claim. In order to extract those roles into the expected permissions in the TokenPayload, this permission_extractor can be used.

Here is an example decoded token from Keycloak (with some stuff removed to improve readability):

{
  "exp": 1728627701,
  "iat": 1728626801,
  "jti": "24fdb7ef-d773-4e6b-982a-b8126dd58af7",
  "sub": "dfa64115-40b5-46ab-924c-c376e73f631d",
  "azp": "my-client",
  "resource_access": {
    "my-client": {
      "roles": [
        "read:stuff"
      ]
    },
  },
}

This extractor would extract the roles ["read:stuff"] as the permissions for the TokenPayload returned by the TokenDecoder.

armasec.token_manager

This module defines a TokenManager that can be used to extract token payloads from request headers.

TokenManager

Handle auth via a TokenDecoder and manage extraction from request headers and serialization into TokenPayload instances.

__init__
__init__(openid_config: OpenidConfig, token_decoder: TokenDecoder, audience: Optional[str] = None, debug_logger: Optional[Callable[..., None]] = None, decode_options_override: Optional[dict] = None)

Initialize a base TokenManager.

Parameters:

Name Type Description Default
openid_config OpenidConfig

The openid_configuration needed for claims such as 'issuer'.

required
token_decoder TokenDecoder

The decoder used to verify jwts

required
audience Optional[str]

An optional audience to check in decoded tokens.

None
debug_logger Optional[Callable[..., None]]

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug

None
decode_options_override Optional[dict]

Options that can override the default behavior of the jwt decode method. For example, one can ignore token expiration by setting this to { "verify_exp": False }

None
extract_token_payload
extract_token_payload(headers: Union[Headers, dict]) -> TokenPayload

Retrieve a token from a request header and decode it into a TokenPayload.

Parameters:

Name Type Description Default
headers Union[Headers, dict]

The headers from which to retrieve a JWT.

required
unpack_token_from_header
unpack_token_from_header(headers: Union[Headers, dict]) -> str

Unpack a JWT from a request header.

Parameters:

Name Type Description Default
headers Union[Headers, dict]

The headers from which to unpack a JWT.

required

armasec.token_payload

This module defines a pydantic schema for the payload of a jwt.

TokenPayload

Bases: BaseModel

A convenience class that can be used to access parts of a decoded jwt.

Attributes:

Name Type Description
sub str

The "sub" claim from a JWT.

permissions List[str]

The permissions claims extracted from a JWT.

expire Optional[datetime]

The "exp" claim extracted from a JWT.

client_id Optional[str]

The "azp" claim extracted from a JWT.

original_token Optional[str]

The original token value

to_dict
to_dict()

Convert a TokenPayload to the equivalent dictionary returned by jwt.decode().

armasec.token_security

This module defines a TokenSecurity injectable that can be used enforce access on FastAPI routes.

ManagerConfig

Bases: BaseModel

Model class to represent a TokenManager instance and its domain configuration for easier mapping

Attributes:

Name Type Description
manager TokenManager

The TokenManager instance to use for decoding tokens.

domain_config DomainConfig

The DomainConfig for the openid server.

PermissionMode

Bases: AutoNameEnum

Endpoint permissions.

Attributes:

Name Type Description
ALL

Require all listed permissions.

SOME

Require at least one of the listed permissions.

TokenSecurity

Bases: APIKeyBase

An injectable Security class that returns a TokenPayload when used with Depends().

Attributes:

Name Type Description
manager Optional[TokenManager]

The TokenManager to use for token validation and extraction.

__call__ async
__call__(request: Request) -> TokenPayload

This method is called by FastAPI's dependency injection system when a TokenSecurity instance is injected to a route endpoint via the Depends() method. Lazily loads the OIDC config, the TokenDecoder, and the TokenManager if they are not already initialized.

Parameters:

Name Type Description Default
request Request

The FastAPI request to check for secure access.

required
__init__
__init__(domain_configs: List[DomainConfig], scopes: Optional[Iterable[str]] = None, permission_mode: PermissionMode = PermissionMode.ALL, debug_logger: Optional[Callable[..., None]] = None, debug_exceptions: bool = False, skip_plugins: bool = False)

Initializes the TokenSecurity instance.

Parameters:

Name Type Description Default
domain_configs List[DomainConfig]

List of domain configuration to authenticate the tokens against.

required
scopes Optional[Iterable[str]]

Optional permissions scopes that should be checked

None
permission_mode PermissionMode

The PermissionMode to apply in the protected route.

ALL
debug_logger Optional[Callable[..., None]]

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug

None
debug_exceptions bool

If True, raise original exceptions. Should only be used in a testing or debugging context.

False
skip_plugins bool

If True, do not evaluate plugin validators.

False

armasec.utilities

Provides some utility functions.

log_error

log_error(logger: Callable[..., None], dep: DoExceptParams)

Logs an en error with the supplied message, a string representation of the error, and its traceback. If the logger supplied is noop, do nothing. Pass as a partial when using the Buzz handle_errors context manager::

with Buzz.handle_errors("Boom!", do_except=partial(log_error, debug_logger)):
    do_some_risky_stuff()

noop

noop(*args, **kwargs)

This is a no-op function that...does nothing.

armasec.schemas.armasec_config

This module provides a pydantic schema describing Armasec's configuration parameters.

DomainConfig

Bases: BaseModel

This model provides a specification for the input domains to authenticate against. It expects the domain indeed and the audience to refer to.

Attributes:

Name Type Description
domain str

The OIDC domain from which resources are loaded.

audience Optional[str]

Optional designation of the token audience.

algorithm str

The Algorithm to use for decoding. Defaults to RS256.

use_https bool

If true, use https for URLs. Otherwise use http

match_keys Dict[str, Union[str, List[Any], Dict[Any, Any], Set[Any], bool, int, float]]

Dictionary of k/v pairs to match in the token when decoding it.

armasec.schemas.jwks

This module provides pydantic schemas for JSON Web Keys.

JWK

Bases: BaseModel

This Model provides a specification for the objects retrieved from JWK endpoints in OIDC providers. It also assists with validation and item access.

Attributes:

Name Type Description
alg str

The algorithm to use for hash validation.

e str

The exponent parameter to use in RS256 hashing

kid str

The "kid" claim to uniquely identify the key.

kty str

The "kty" claim to identify the type of the key.

n str

The modulus parameter to use in RS256 hashing.

use Optional[str]

The claim that identifies the intended use of the public key.

x5c Optional[List[str]]

The X.509 certificate chain parameter

x5c Optional[List[str]]

The X.509 certificate SHA-1 thumbprint parameter

JWKs

Bases: BaseModel

This Model provides a specification for the container object retrieved from JWK endpoints in OIDC providers. It also assists with validation and item access.

Attributes:

Name Type Description
keys List[JWK]

The list of JWKs contained within.

armasec.schemas.openid_config

This module provides a pydantic schema describing openid-configuration data.

OpenidConfig

Bases: BaseModel

Provides a specification for the objects retrieved from openid_configuration endpoint of the OIDC providers. Only includes needed fields for supported Manager instances. Assists with validation and item access.

Attributes:

Name Type Description
issuer AnyHttpUrl

The URL of the issuer of the tokens.

jwks_uri AnyHttpUrl

The URI where JWKs can be foun don the OpenID server.

armasec.pluggable

Manage plugins from armasec.

armasec.pluggable.hookspecs

Hook specification module for armasec plugins.

armasec_plugin_check

armasec_plugin_check(request: Request, token_payload: TokenPayload, debug_logger: Callable[..., None]) -> None

Check a token payload for validity against a request using your plugin.

If the check fails, it should raise a ArmasecError or a subclass thereof.

Parameters:

Name Type Description Default
request Request

The original request made to the secured endpoint. Will be passed to the plugin method if the implementation includes it as a keyword argument.

required
token_payload TokenPayload

The contents of the auth token. Will be passed ot the plugin method if the implementation includes it as a keyword argument

required
debug_logger Callable[..., None]

A callable, that if provided, will allow debug logging. Should be passed as a logger method like logger.debug. Will be passed to the plugin method if the implementation includes it as a keyword argument.

required