crossauth_backend.common package¶
Submodules¶
crossauth_backend.common.error module¶
- exception crossauth_backend.common.error.CrossauthError(code: ErrorCode, message: str | list[str] | None = None)[source]¶
Bases:
ExceptionThrown by Crossauth functions whenever it encounters an error.
- static as_crossauth_error(e: Any, default_message: str | None = None) CrossauthError[source]¶
If the passed object is a crossauth_backend.CrossauthError instance, simply returns it. If not and it is an object with errorCode in it, creates a CrossauthError from that and errorMessage, if present. Otherwise creates a crossauth_backend.CrossauthError object with
ErrorCodeof Unknown from it, setting the message if possible.- Parameters:
e (Any) – the error to convert.
default_message (str|None) – message to use if there was none in the original exception.
- Returns:
a
crossauth_backend.CrossauthErrorinstance.
- property code_name¶
Return the name of the error code
- static from_oauth_error(error: str, error_description: str | None = None) CrossauthError[source]¶
OAuth defines certain error types. To convert the error in an OAuth response into a CrossauthError object, call this function.
- Parameters:
error (str) – as returned by an OAuth call (converted to an
ErrorCode).
- :param str error_description as returned by an OAuth call (put in the
message)
:return a crossauth_backend.CrossauthError instance.
- static http_status_name(status: str | int) str[source]¶
Returns the friendly name for an HTTP response code.
If it is not a recognized one, returns the friendly name for 500. @param status the HTTP response code, which, while being numeric,
can be in a string or number.
@returns the string version of the response code.
- property oauthErrorCode: str¶
Return the OAuth name of an error code (eg “server_error”)
- class crossauth_backend.common.error.ErrorCode(*values)[source]¶
Bases:
EnumIndicates the type of error reported by
crossauth_backend.CrossauthError- AuthorizationPending = 44¶
Thrown in the OAuth device code flow
- BadRequest = 43¶
Thrown when an invalid request is made, eg configure 2FA when 2FA is switched off for user
- ClientExists = 6¶
This is returned if attempting to make a client which already exists (client_id or name/userid)
- Configuration = 31¶
Thrown when something is missing or inconsistent in configuration
- Connection = 24¶
Thrown when there is a connection error, eg to a database
- ConstraintViolation = 47¶
Thrown in database handlers where an insert causes a constraint violation
- DataFormat = 39¶
Thrown when a the data field of key storage is not valid json
- EmailNotExist = 3¶
Thrown when a a password reset is requested and the email does not exist
- EmailNotVerified = 12¶
Thrown on login attempt with a user account marked not having had the email address validated
- Expired = 23¶
Thrown when a session or API key has expired
- ExpiredToken = 46¶
Thrown in the OAuth device code flow
- Factor2ResetNeeded = 30¶
Thrown if the user needs to reset factor2 before logging in
- FetchError = 40¶
Thrown if a fetch failed
- Forbidden = 19¶
Returned with an HTTP 403 response
- FormEntry = 42¶
Thrown by user-supplied validation functions if a user details form was incorrectly filled out
- InsufficientPriviledges = 18¶
Returned if user is valid but doesn’t have permission to access resource
- InsufficientScope = 17¶
Thrown for the OAuth insufficient_scope error
- InvalidClientId = 5¶
This is returned if an OAuth2 client id is invalid
- InvalidClientIdOrSecret = 8¶
Server endpoints in this package will return this instead of InvalidClientId or InvalidClientSecret for security purposes
- InvalidClientSecret = 7¶
This is returned if an OAuth2 client secret is invalid
- InvalidCsrf = 21¶
Thrown if the CSRF token is invalid
- InvalidEmail = 32¶
Thrown if an email address in invalid
- InvalidHash = 25¶
Thrown when a hash, eg password, is not in the given format
- InvalidKey = 20¶
Thrown when a session or API key was provided that is not in the key table. For CSRF and sesison key, an InvalidCsrf or InvalidSession will be thrown instead
- InvalidOAuthFlow = 10¶
This is returned a request is made with a an oauth flow name that is not recognized
- InvalidPhoneNumber = 33¶
Thrown if a phone number in invalid
- InvalidRedirectUri = 9¶
This is returned a request is made with a redirect Uri that is not registered
- InvalidScope = 16¶
Thrown for the OAuth invalid_scope error
- InvalidSession = 22¶
Thrown if the session cookie is invalid
- InvalidToken = 36¶
Thrown when a token (eg TOTP or OTP) is invalid
- InvalidUsername = 34¶
Thrown if an email address in invalid
- KeyExists = 27¶
Thrown if you try to create a key which already exists in key storage
- MfaRequired = 37¶
Thrown during OAuth password flow if an MFA step is needed
- NotImplemented = 48¶
Thrown if a method is unimplemented, typically when a feature is not yet supported in this language.
- PasswordChangeNeeded = 28¶
Thrown if the user needs to reset his or her password
- PasswordFormat = 38¶
Thrown when a password does not match rules (length, uppercase/lowercase/digits)
- PasswordInvalid = 2¶
Thrown when a password does not match, eg during login or signup
- PasswordMatch = 35¶
Thrown when two passwords do not match each other (eg signup)
- PasswordResetNeeded = 29¶
Thrown if the user needs to reset his or her password
- SlowDown = 45¶
Thrown in the OAuth device code flow
- TwoFactorIncomplete = 13¶
Thrown on login attempt with a user account marked not having completed 2FA setup
- Unauthorized = 14¶
Thrown when a resource expecting user authorization was access and authorization not provided or wrong
- UnauthorizedClient = 15¶
Thrown for the OAuth unauthorized_client error (when the client is unauthorized as opposed to the user)
- UnknownError = 50¶
Thrown for an condition not convered above.
- UnsupportedAlgorithm = 26¶
Thrown when an algorithm is requested but not supported, eg hashing algorithm
- UserExists = 41¶
Thrown when attempting to create a user that already exists
- UserNotActive = 11¶
Thrown on login attempt with a user account marked inactive
- UserNotExist = 1¶
Thrown when a given username does not exist, eg during login
- UsernameOrPasswordInvalid = 4¶
For endpoints provided by servers in this package, this is returned instead of UserNotExist or PasswordNotMatch, for security reasons
- ValueError = 49¶
Thrown a dict field is unexpectedly missing or wrong type
crossauth_backend.common.interfaces module¶
- class crossauth_backend.common.interfaces.ApiKey[source]¶
Bases:
KeyAn API key is a str that can be used in place of a username and password. These are not automatically created, like OAuth access tokens.
- created: datetime¶
- data: NotRequired[str]¶
- expires: datetime | NullType¶
- lastactive: NotRequired[datetime]¶
- name: str¶
A name for the key, unique to the user
- userid: NotRequired[str | int | NullType]¶
- value: str¶
- class crossauth_backend.common.interfaces.Key[source]¶
Bases:
TypedDictA key (eg session ID, email reset token) as stored in a database table.
The fields defined here are the ones used by Crossauth. You may add others.
- created: datetime¶
The datetime/time the key was created, in local time on the server
- data: NotRequired[str]¶
Additional key-specific data (eg new email address for email change).
While application specific, any data Crossauth puts in this field is a strified JSON, with its own key so it can co-exist with other data.
- expires: datetime | NullType¶
The datetime/time the key expires
- lastactive: NotRequired[datetime]¶
The datetime/time key was last used (eg last time a request was made with this value as a session ID)
- userid: NotRequired[str | int | NullType]¶
the user this key is for (or undefined for an anonymous session ID)
It accepts the value null as usually this is the value stored in the database, rather than undefined. Some functions need to differentiate between a null value as opposed to the value not being defined (eg for a partial updatetime).
- value: str¶
The value of the keykey. In a cookie, the value part of cookiename=value; options…
- class crossauth_backend.common.interfaces.KeyPrefix[source]¶
Bases:
objectThese are used prefixes for keye in a key storage entry
- access_token = 'access:'¶
- api_key = 'api:'¶
- authorization_code = 'authz:'¶
- device_code = 'dc:'¶
- email_verification_token = 'e:'¶
- mfa_token = 'omfa:'¶
- password_reset_token = 'p:'¶
- refresh_token = 'refresh:'¶
- session = 's:'¶
- user_code = 'uc:'¶
- class crossauth_backend.common.interfaces.OAuthClient[source]¶
Bases:
TypedDictOAuth client data as stored in a database table
- client_id: str¶
The client_id, which is auto-generated and immutable
- client_name: str¶
A user-friendly name for the client (not used as part of the OAuth API).
- client_secret: NotRequired[str | NullType]¶
Client secret, which is autogenerated.
If there is no client secret, it should be set to undefined.
This field allows null as well as undefined this is used, for example, when partially updating a client and you specifically want to set the secret to undefined, as opposed to just not wishing to change the value. Other than that, this value is always either a str or undefined.
- confidential: bool¶
Whether or not the client is confidential (and can therefore keep the client secret secret)
- redirect_uri: list[str]¶
An array of value redirect URIs for the client.
- userid: NotRequired[str | int | NullType]¶
ID of the user who owns this client, which may be undefined for not being owned by a specific user.
This field allows null as well as undefined this is used, for example, when partially updating a client and you specifically want to set the user ID to undefined, as opposed to just not wishing to change the value. Other than that, this value is always either a str or number (depending on the ID type in your user storage) or undefined.
- valid_flow: list[str]¶
An array of OAuth flows allowed for this client.
See
OAuthFlows.
- class crossauth_backend.common.interfaces.PartialKey[source]¶
Bases:
TypedDictSame as
crossauth_backend.Keybut all fields are NotRequired- created: datetime¶
- data: str | None¶
- expires: datetime | NullType¶
- lastactive: datetime | None¶
- userid: str | int | NullType | None¶
- value: str¶
- class crossauth_backend.common.interfaces.PartialUser[source]¶
Bases:
PartialUserInputFieldsSame as User but all fields are not required
- admin: NotRequired[bool]¶
- email: NotRequired[str]¶
- factor1: NotRequired[str]¶
- factor2: NotRequired[str]¶
- id: str | int¶
- state: str¶
- username: str¶
- class crossauth_backend.common.interfaces.PartialUserInputFields[source]¶
Bases:
TypedDictSame as UserInputFields but all fields are not required
- admin: NotRequired[bool]¶
- email: NotRequired[str]¶
- factor1: NotRequired[str]¶
- factor2: NotRequired[str]¶
- state: str¶
- username: str¶
- class crossauth_backend.common.interfaces.PartialUserSecrets[source]¶
Bases:
UserSecretsInputFieldsSame as UserSecrets except all fields are NotRequired
- expiry: int¶
- otp: str¶
- password: str¶
- totpsecret: str¶
- userid: str | int¶
- class crossauth_backend.common.interfaces.User[source]¶
Bases:
UserInputFieldsThis adds ID to
UserInputFields.If your username field is unique and immutable, you can omit ID (passing username anywhere an ID) is expected. However, if you want users to be able to change their username, you should include ID field and make that immutable instead.
- admin: NotRequired[bool]¶
- email: NotRequired[str]¶
- factor1: str¶
- factor2: NotRequired[str]¶
- id: str | int¶
- state: str¶
- username: str¶
- class crossauth_backend.common.interfaces.UserInputFields[source]¶
Bases:
TypedDictDescribes a user as fetched from the user storage (eg, database table), excluding auto-generated fields such as an auto-generated ID
This is extendible with additional fields - provide them to the
UserStorageclass as extraFields.You may want to do this if you want to pass additional user data back to the caller, eg real name.
The fields defined here are the ones used by Crossauth. You may add others.
- admin: NotRequired[bool]¶
Whether or not the user has administrator priviledges (and can acess admin-only functions).
- email: NotRequired[str]¶
You can optionally include an email address field in your user table. If your username is an email address, you do not need a separate field.
- factor1: str¶
Factor for primary authentication.
Should match the name of an authenticator
- factor2: NotRequired[str]¶
Factor for second factor authentication.
Should match the name of an authenticator
- state: str¶
You are free to define your own states. The ones Crossauth recognises are defined in
UserState.
- username: str¶
The username. This may be an email address or anything else, application-specific.
- class crossauth_backend.common.interfaces.UserSecrets[source]¶
Bases:
UserSecretsInputFieldsThis adds the user ID toi
UserSecretsInputFields.- expiry: int¶
- otp: str¶
- password: str¶
- totpsecret: str¶
- userid: str | int¶
- class crossauth_backend.common.interfaces.UserSecretsInputFields[source]¶
Bases:
TypedDictSecrets, such as a password, are not in the User object to prevent them accidentally being leaked to the frontend. All functions that return secrets return them in this separate object.
The fields in this class are the ones that are not autogenerated by the database.
- expiry: int¶
- otp: str¶
- password: str¶
- totpsecret: str¶
- class crossauth_backend.common.interfaces.UserState[source]¶
Bases:
objectThese are used valiues for state in a
crossauth_backend.Userobject- active = 'active'¶
- awaiting_email_verification = 'awaitingemailverification'¶
- awaiting_two_factor_setup = 'awaitingtwofactorsetup'¶
- disabled = 'disabled'¶
- factor2_reset_needed = 'factor2resetneeded'¶
- password_and_factor2_reset_needed = 'passwordandfactor2resetneeded'¶
- password_change_needed = 'passwordchangeneeded'¶
- password_reset_needed = 'passwordresetneeded'¶
crossauth_backend.common.jwt module¶
crossauth_backend.common.logger module¶
- class crossauth_backend.common.logger.CrossauthLogger(level: int | None = None)[source]¶
Bases:
CrossauthLoggerInterfaceA very simple logging class with no dependencies.
Logs to console.
The logging API is designed so that you can replace this with other common loggers, eg Pino. To change it, use the global
CrossauthLogger.set_logger()function. This has a parameter to tell Crossauth whether your logger accepts JSON input or not.When writing logs, we use the helper function
j()to send JSON to the logger if it is supprted, and a stringified JSON otherwise.Crossauth logs
All Crossauth log messages are JSON (or stringified JSON, depending on whether the logger supports JSON input - this one does). The following fields may be present depending on context (msg is always present):
msg : main contents of the log
- erran error object. If a subclass of Error, it wil contain at least message and
a stack trace in stack. If the error is of type
crossauth_backend.CrossauthErrorit also will also contain code and http_status.
- hashedSessionCookiefor security reasons, session cookies are not included in logs.
However, so that errors can be correlated with each other, a hash of it is included in errors originating from a session.
- hashedCsrfCookiefor security reasons, csrf cookies are not included in logs.
However, so that errors can be correlated with each other, a hash of it is included in errors originating from a session.
user : username
emailMessageId : internal id of any email that is sent
email : email address
userid : sometimes provided in addition to username, or when username not available
- hahedApiKeya hash of an API key. The unhashed version is not logged for security,
but a hash of it is logged for correlation purposes.
- headeran HTTP header that relates to an error (eg Authorization), only if
it is non-secret or invalid
- accessTokenHashhash of the JTI of an access token. For security reasons, the
unhashed version is not logged.
method: request method (GET, PUT etc)
url : relevant URL
ip : relevant IP address
scope : OAuth scope
error_code : Crossauth error code
error_code_name : String version of Crossauth error code
http_status : HTTP status that will be returned
port port service is running on (only for starting a service)
prefix prefix for endpoints (only when starting a service)
authorized whether or not a valid OAuth access token was provided
- levelName = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG']¶
- static logger() CrossauthLoggerInterface[source]¶
Returns the static logger instance
- rossauth_logger_accepts_json = True¶
- static set_logger(logger: CrossauthLoggerInterface, accepts_json: bool) None[source]¶
Set the static logger instance