Skip to content

security.auth

Validation of bearer tokens (JSON Web Tokens, JWTs) issued by OpenID Connect identity providers.

security:
  auth:
    issuers:
      - https://login.example.org/oidc/
    audience:
      - my-app
    verify_audience: true
    token_types:
      - at+jwt
      - application/at+jwt
    verify_token_type: true
    clients: null
    claim_identity: sub
    algorithms:
      - RS256
    allow_expired: false
    validate_userinfo: false
    userinfo_claims: []
    service_clients:
      - client_id: conquest-client-id
        issuer: null
        subject: null
        on_behalf_of: false
    on_behalf_of_header: null
    timeout: 10
    cache_ttl: 300

Only tokens issued by one of the trusted issuers are accepted; all others are rejected without contacting their issuers. With trusted issuers, audience is required, so that tokens issued for other apps by the same identity provider are rejected; to accept tokens for any audience instead (not recommended, unless clients are restricted), set verify_audience: false. Only OAuth 2.0 access tokens as per RFC 9068 (typ header at+jwt or application/at+jwt) are accepted, so that other JWTs of the same identity provider, such as ID tokens, cannot be used as access tokens; to accept tokens of other types, list them in token_types, or set verify_token_type: false if the identity provider does not set the typ header of its access tokens. To accept only tokens obtained by specific clients (client_id claim, or azp claim if there is none), e.g., by a gateway in front of the app, list their IDs in clients, in addition to audience or instead of it (with verify_audience: false). Clients calling the app with client-credentials tokens of their own are listed as service_clients; no user info is requested for their tokens. Each service client is tied to a trusted issuer, which is required if several issuers are trusted (cf. Service tokens). See Authentication for how tokens are validated and how to require them for operations.

Issuers are listed as URLs, or with settings of their own, e.g., to choose the namespace of their subjects (cf. several trusted issuers). Identity providers differ, so issuers may also have an audience, a group claim (claim_groups, cf. security.access_control) and userinfo_claims of their own, which replace the global settings for their tokens; unset ones fall back to the global settings. An empty userinfo_claims adds no claims from the issuer's user info, and an empty claim_groups takes no groups from its tokens:

security:
  auth:
    audience:
      - my-app
    issuers:
      - issuer: https://login.example.org/oidc/
        namespace: ""
      - issuer: https://login.example.com/oidc/
        namespace: example-com
        audience:
          - my-app-client-id
        claim_groups: groups
        userinfo_claims: []

With an audience for each issuer, the global audience is not required.

AuthConfig

Bases: FOCABaseConfig

Model for configuring JSON Web Token (JWT)-based authorization.

Bearer tokens are only accepted if issued by one of the trusted issuers. They are verified locally with the issuer's public keys, obtained via OpenID Connect discovery, and, if validate_userinfo is set, additionally via the issuer's user info endpoint.

Which operations require a bearer token is defined by the security requirements of the operations in the API specifications (e.g., via x-bearerInfoFunc: foca.security.auth.validate_token).

Parameters:

Name Type Description Default
issuers

Trusted token issuers, i.e., values of the iss claim of accepted tokens, as URLs or with settings of their own (cf. :py:class:IssuerConfig). Tokens of other issuers are rejected without contacting them. If empty, all tokens are rejected. With several issuers, subjects are namespaced by issuer (cf. IssuerConfig.namespace); issuers must have different namespaces, and at most one may keep its subjects bare.

required
add_key_to_claims

Whether to add the identity provider's public key that the token was verified with, in PEM format, to the claims.

required
allow_expired

Allow/disallow expired JWTs. If False, a 401 authorization error is raised in response to a request containing an expired JWT.

required
audience

List of audiences that the app identifies itself with. JWTs whose aud claim does not contain any of them are rejected. Required if issuers are set, unless verify_audience is False or all issuers have an audience of their own (cf. :py:class:IssuerConfig), which replaces this one for their tokens.

required
verify_audience

Whether to validate the audience of JWTs. If False, tokens that trusted issuers issued for other services are accepted, too; only disable it if the issuer does not include the app in the audience of its tokens, or if clients are restricted instead.

required
token_types

Accepted values of the typ header of JWTs, compared case-insensitively. By default, only OAuth 2.0 access tokens as per RFC 9068 (at+jwt or application/at+jwt) are accepted, so that other JWTs of trusted issuers, such as ID tokens, cannot be used as access tokens. Required unless verify_token_type is False.

required
verify_token_type

Whether to validate the type of JWTs. If False, JWTs with any or no typ header are accepted; only disable it if the issuer does not set the typ header of its access tokens.

required
clients

Accepted clients, i.e., IDs of the clients that obtained the tokens, as per the client_id claim (RFC 9068) of JWTs or, if they have none, the azp claim (OpenID Connect), compared exactly. If set, JWTs of other clients, and JWTs with neither claim, are rejected, e.g., so that a service behind a gateway accepts only tokens obtained by the gateway. Can be used in addition to audience, or instead of it with verify_audience set to False. Client IDs are matched for all trusted issuers, so with several issuers, make sure that the listed IDs cannot be claimed by clients of another issuer. If None, clients are not restricted.

required
claim_identity

The JWT claim used to identify the sender. Tokens without this claim are rejected.

required
algorithms

Lists the JWT-signing algorithms supported by the app.

required
validate_userinfo

Whether to additionally validate tokens via the issuer's user info endpoint, which needs to report the token's subject.

required
userinfo_claims

Claims to add from the issuer's user info endpoint if the token lacks them, e.g., eduperson_entitlement, for access control based on group claims (cf. access_control.claim_groups) with identity providers that only provide them via user info. Claims of the token are never replaced. The user info needs to report the token's subject; it is cached per token for cache_ttl seconds (unless validate_userinfo is set), but not beyond the token's expiry. The claims client_id and azp cannot be added, as they identify the client that obtained the token (cf. clients and service_clients). Issuers may have userinfo_claims of their own (cf. :py:class:IssuerConfig), which replace these for their tokens.

required
timeout

Timeout (in seconds) for requests to identity providers.

required
cache_ttl

Time (in seconds) for which identity providers' discovery documents and public keys, and user info, are cached.

required
service_clients

Service clients, whose client-credentials tokens (service tokens) represent the client, not a user (cf. :py:class:ServiceClientConfig). No user info is requested for service tokens (neither for userinfo_claims nor for validate_userinfo), and access control enforces policies on them with the subject client:<client ID> (cf. AccessControlConfig.client_mapping). Client IDs must be unique.

required
on_behalf_of_header

Request header in which service clients allowed to (cf. ServiceClientConfig.on_behalf_of) name the user they act for (e.g., X-On-Behalf-Of). If None, only the app can pass a named user (e.g., from a request field) to :py:func:foca.security.access_control.get_on_behalf_of_user.

required

Attributes:

Name Type Description
issuers Sequence[str | IssuerConfig]

Trusted token issuers.

add_key_to_claims bool

Whether to add the identity provider's public key that the token was verified with, in PEM format, to the claims.

allow_expired bool

Allow/disallow expired JWTs.

audience list[str] | None

List of audiences that the app identifies itself with.

verify_audience bool

Whether to validate the audience of JWTs.

token_types list[str]

Accepted values of the typ header of JWTs.

verify_token_type bool

Whether to validate the type of JWTs.

clients list[str] | None

Accepted clients, i.e., IDs of the clients that obtained the tokens.

claim_identity str

The JWT claim used to identify the sender.

algorithms list[str]

Lists the JWT-signing algorithms supported by the app.

validate_userinfo bool

Whether to additionally validate tokens via the issuer's user info endpoint.

userinfo_claims list[str]

Claims to add from the issuer's user info endpoint if the token lacks them.

timeout float

Timeout (in seconds) for requests to identity providers.

cache_ttl float

Time (in seconds) for which identity providers' discovery documents and public keys, and user info, are cached.

service_clients list[ServiceClientConfig]

Service clients.

on_behalf_of_header str | None

Request header in which service clients name the user they act for.

Raises:

Type Description
ValidationError

The class was instantianted with an illegal data type, issuers are set without audience while verify_audience is True, token_types are empty while verify_token_type is True, clients are set but empty (or blank), client IDs of service_clients are not unique, service clients are not among the accepted clients, the issuer of a service client is not trusted or, with several trusted issuers, not set, userinfo_claims contain client_id or azp, an issuer is listed with different settings, or issuers have the same namespace (also none).

Examples:

>>> auth = AuthConfig(
...     issuers=["https://login.example.org/oidc/"],
...     audience=["my-app"],
... )
>>> auth.audience
['my-app']
>>> auth.algorithms
['RS256']
>>> auth.token_types
['at+jwt', 'application/at+jwt']

IssuerConfig

Bases: FOCABaseConfig

Model for configuring a trusted token issuer.

Plain URLs in AuthConfig.issuers stand for issuers with the default settings.

Parameters:

Name Type Description Default
issuer

Issuer, i.e., value of the iss claim of its tokens.

required
namespace

Namespace of the subjects of the issuer's tokens, as <namespace>|<subject> (cf. :py:func:foca.security.auth.namespace_subject), so that equal subjects of different issuers are different users. If None (default), the issuer if several issuers are trusted, else none. An empty value keeps the issuer's subjects bare, e.g., for the first issuer of an app that trusts a second one later, so that its stored subjects stay valid. Must not contain |, commas or line breaks, and must have at most 256 characters (MAX_NAMESPACE_LENGTH), so that namespaced subjects fit into policy rules.

required
audience

Accepted audiences of the issuer's tokens (cf. AuthConfig.audience). If None (default), AuthConfig.audience applies.

required
claim_groups

Claim of the issuer's tokens listing groups or entitlements of the subject (cf. AccessControlConfig.claim_groups). If None (default), AccessControlConfig.claim_groups applies. An empty value takes no groups from the issuer's tokens, e.g., for an issuer trusted for login only, whose group claim values could collide with those of other issuers.

required
userinfo_claims

Claims to add from the issuer's user info if its tokens lack them (cf. AuthConfig.userinfo_claims). If None (default), AuthConfig.userinfo_claims applies; an empty list adds none.

required

Attributes:

Name Type Description
issuer str

Issuer.

namespace str | None

Namespace of the subjects of the issuer's tokens.

audience list[str] | None

Accepted audiences of the issuer's tokens.

claim_groups str | None

Claim of the issuer's tokens listing groups.

userinfo_claims list[str] | None

Claims to add from the issuer's user info.

Raises:

Type Description
ValidationError

The class was instantiated with an illegal data type, issuer is empty, namespace contains |, a comma or a line break or is too long, audience is empty or has blank audiences, claim_groups has surrounding whitespace, or userinfo_claims contain client_id or azp.

Examples:

>>> conf = IssuerConfig(issuer="https://login.example.org/", namespace="ls")
>>> conf.namespace, conf.audience
('ls', None)

ServiceClientConfig

Bases: FOCABaseConfig

Model for a service client, i.e., a client using its own tokens.

Services call each other with OAuth 2.0 client-credentials tokens (e.g., a workflow engine calling a task service), which represent the client, not a user, and have no user info. A token is a service token of a service client if it was issued by the client's issuer (its iss claim), to the client (its client_id claim, or azp without client_id, is client_id), and its subject (the claim configured as AuthConfig.claim_identity) is subject. Other tokens issued to the client (e.g., for users, with their subjects) are user tokens. Check which claims your identity provider puts into client-credentials tokens, and set subject accordingly.

Parameters:

Name Type Description Default
client_id

Client ID of the service client.

required
issuer

Issuer of the client's service tokens (iss claim), one of the trusted AuthConfig.issuers, as client IDs are only unique per issuer. Required if several issuers are trusted; if None, the only trusted issuer.

required
subject

Subject of the client's service tokens. If None, the client ID.

required
on_behalf_of

Whether the client may name the user it acts for (cf. AuthConfig.on_behalf_of_header and :py:func:foca.security.access_control.get_on_behalf_of_user), e.g., so that the app makes that user the owner of a resource the client creates. Such clients can also list the resources of any user they name (cf. :py:func:foca.security.access_control.resource_filter), so only allow fully trusted clients.

required

Attributes:

Name Type Description
client_id str

Client ID of the service client.

issuer str | None

Issuer of the client's service tokens.

subject str | None

Subject of the client's service tokens.

on_behalf_of bool

Whether the client may name the user it acts for.

Raises:

Type Description
ValidationError

The class was instantianted with an illegal data type, or client_id, issuer or subject is empty.

Examples:

>>> client = ServiceClientConfig(client_id="conquest")
>>> client.issuer is None and client.subject is None
True
>>> client.on_behalf_of
False