1. Backend
  2. Python Library Reference

PropelAuth’s Python library provides all the building blocks you need to add authentication to any Python backend.

For most Python frameworks, like FastAPI, Django, and Flask, we have built out libraries specifically for them. Those libraries will provide a more first-class experience than this library.

Installation

pip install propelauth_py

Initialize

init_base_auth performs a one-time initialization of the library. It will verify your api_key is correct and fetch the metadata needed to verify access tokens in validate_access_token_and_get_user and validate_access_token_and_get_user_with_org.

from propelauth_py import init_base_auth

auth = init_base_auth("YOUR_AUTH_URL", "YOUR_API_KEY")

Protect API routes

validate_access_token_and_get_user

This function takes in the Authorization HTTP header and verifies the request was made by a valid user. It expects the header to be in the format Bearer ACCESS_TOKEN. If the Authorization HTTP header is malformed or doesn’t contain a valid access token, an UnauthorizedException is thrown.

Otherwise, a User is returned.

auth_header = # get authorization header in the form `Bearer {TOKEN}`
try:
   user = auth.validate_access_token_and_get_user(auth_header)
   print("Logged in as", user.user_id)
except UnauthorizedException:
   print("Invalid access token")

validate_access_token_and_get_user_with_org

This function will verify that a request was made by a valid user AND that user is in an organization.

ArgumentDescription
Authorization HTTP HeaderIt expects the header to be in the format Bearer ACCESS_TOKEN. If the Authorization HTTP header is malformed or doesn’t contain a valid access token, an UnauthorizedException is thrown.
Required Org IDA string representing the organization ID to check that the user is a member of. If the user is NOT a member of the specified organization, a ForbiddenException is thrown.

The return value has a user and a org_member_info containing the following properties:

FieldDescription
org_idThe id of the org
org_nameThe name of the org
user_assigned_roleThe user’s role within the organization. See Roles and Permissions for more details.
user_is_role(role: str): boolReturns True if the user’s role within the organization matches the role passed in
user_is_at_least_role(role: str): boolReturns True if the user’s role within the organization is at least the role passed in. If the hierarchy of roles is Owner => Admin => Member, specifying “Admin” will return True for Admins and Owners, but False for Members.
user_has_permission(permission: str): boolReturn True if the user has a specific permission. The users’ permissions are derived from their role within this organization.
user_has_all_permissions(permissions: List[str]): boolReturn True if the user has all the specified permissions. The users’ permissions are derived from their role within this organization.

User

A user returned by validate_access_token_and_get_user and validate_access_token_and_get_user_with_org.

PropertyDescription
user_idThe id of the user
org_id_to_org_member_infoA dictionary of org ids to metadata about the org. Includes all orgs that the user is in
legacy_user_idThe original ID of the user, if the user was migrated from an external source

The values of orgIdToOrgMemberInfo are OrgMemberInfo’s, with the following fields/functions:

FieldDescription
org_idThe id of the org
org_nameThe name of the org
user_assigned_roleThe user’s role within the organization. See Roles and Permissions for more details.
user_is_role(role: str): boolReturns True if the user’s role within the organization matches the role passed in
user_is_at_least_role(role: str): boolReturns True if the user’s role within the organization is at least the role passed in. If the hierarchy of roles is Owner => Admin => Member, specifying “Admin” will return True for Admins and Owners, but False for Members.
user_has_permission(permission: str): boolReturn True if the user has a specific permission. The users’ permissions are derived from their role within this organization.
user_has_all_permissions(permissions: List[str]): boolReturn True if the user has all the specified permissions. The users’ permissions are derived from their role within this organization.

Roles and Permissions

A user has a Role within an organization. By default, the available roles are Owner, Admin, or Member, but these can be configured. These roles are also hierarchical, so Owner > Admin > Member.

Roles allow you to control what different users can do within your product. If you want to check a user’s role, you can use auth.validate_access_token_and_get_user_with_org_by_minimum_role or auth.validate_access_token_and_get_user_with_org_by_exact_role which will very the request was made by a valid user, that user is in the specified organization, AND that the user’s within the organization matches what was passed in.

Permissions are arbitrary strings associated with a role. For example, can_view_billing, ProductA::CanCreate, and ReadOnly are all valid permissions. The PropelAuth dashboard allows you to set up these permissions.

You can use auth.validate_access_token_and_get_user_with_org_by_permission or auth.validate_access_token_and_get_user_with_org_by_all_permissions to check for a given permission.

All of these functions, just like validate_access_token_and_get_user_with_org, will take in an authorization header and an organization ID. The last parameter is either a role, permission, or list of permissions depending on the function.

Fetch user metadata by id

# auth object somes from init_base_auth
auth.fetch_user_metadata_by_user_id(user_id, include_orgs=False)

Successful response:

{
    "user_id":            "e9d3520f-836e-403c-82c2-09843517e1ce",
    "email":              "user@example.com",
    "email_confirmed":    true,
    "has_password":       true,

    "username":           "example",
    "first_name":         "first",
    "last_name":          "last",
    "picture_url":        "https://...",

    // True if the user's account is locked for security purposes.
    "locked":             false,
    "enabled":            true,
    "mfa_enabled":        false,

    "created_at":         1645131680,
    "last_active_at":     1650654711,

    // Only returned if include_orgs = true
    "org_id_to_org_info": {
        "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4": {
            "org_id": "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
            "org_name": "ExampleOrganization",
            "user_role": "Owner"
        }
    },

    // Only returned if user was migrated from an external system
    "legacy_user_id":    "507f191e810c19729de860ea"
}

Fetch user metadata by email

# auth object somes from init_base_auth
auth.fetch_user_metadata_by_email(email, include_orgs=False)

Successful response is the same as fetch_user_metadata_by_user_id, expect it takes an email as an argument.

Fetch user metadata by username

# auth object somes from init_base_auth
auth.fetch_user_metadata_by_username(email, include_orgs=False)

Successful response is the same as fetch_user_metadata_by_user_id, expect it takes a username as an argument.

Batch fetch users by ids

# auth object somes from init_base_auth
user_id_to_metadata = auth.fetch_batch_user_metadata_by_user_ids([
    "1be238f3-5908-4c51-b3bf-e53dd763047e",
    "beb00acf-6e48-435d-8388-3758607ec01b",
    "941c99e5-3530-4475-bd0f-bbc5d06603c3"
], include_orgs=False)

Any IDs that don’t have a match are not returned. Duplicate users are only returned once.

user_id_to_metadata will store a dictionary response as shown below:

{
    "1be238f3-5908-4c51-b3bf-e53dd763047e": {
        "user_id": "e9d3520f-836e-403c-82c2-09843517e1ce",
        "email": "user@example.com",
        "email_confirmed": true,
        "has_password": true,

        "username": "example",
        "first_name": "first",
        "last_name": "last",
        "picture_url": "https://...",

        // True if the user's account is locked for security purposes.
        "locked": false,
        "enabled": true,
        "mfa_enabled": false,

        "created_at": 1645131680,
        "last_active_at": 1650654711,

        // Only returned if include_orgs = true
        "org_id_to_org_info": {
            "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4": {
                "org_id": "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
                "org_name": "ExampleOrganization",
                "user_role": "Owner",
            }
        },

        // Only returned if user was migrated from an external system
        "legacy_user_id":    "507f191e810c19729de860ea"
    },
    "beb00acf-6e48-435d-8388-3758607ec01b": {
        //...
    }
}

Batch fetch users by emails

# auth object somes from init_base_auth
auth.fetch_batch_user_metadata_by_emails(["a@example.com", "b@example.com"], include_orgs=True)

Successful response is the same as Batch fetch users by ids, but the keys are matching email addresses.

Batch fetch users by usernames

# auth object somes from init_base_auth
auth.fetch_batch_user_metadata_by_usernames(["usera", "userb", "userc"], include_orgs=True)

Successful response is the same as Batch fetch users by ids, but the keys are matching usernames.

Search for users

# auth object somes from init_base_auth
auth.fetch_users_by_query(page_size=10, page_number=0, order_by=UserQueryOrderBy.CREATED_AT_ASC,
                          email_or_username=None, include_orgs=False)
ArgumentDescription
page_sizeThe number of results to return at a time. Must be between 1 and 100, default 10.
page_numberUsed to page over results
order_byHow to order the results. See UserQueryOrderBy for options
email_or_usernameSearches for partial matches within email addresses or usernames. port would match a user with email address support@propelauth.com.
include_orgsWhether or not to include the user’s organization information in the response. Default false

Successful response:

{
    "total_users": 103,
    "current_page": 0,
    "page_size": 10,
    "has_more_results": true,
    "users": [{
        // See (1) for example users
    }, {
        // There will be 10 users in this response
    }]
}
  1. Example user in response

Fetch users in organization

# auth object somes from init_base_auth
auth.fetch_users_in_org(org_id, page_size=10, page_number=0, include_orgs=False)
ArgumentDescription
org_idThe organization to fetch users for
page_sizeThe number of results to return at a time. Must be between 1 and 100, default 10.
page_numberUsed to page over results
include_orgsWhether or not to include the user’s organization information in the response. Default false

Successful response:

{
    "total_users": 43,
    "current_page": 0,
    "page_size": 10,
    "has_more_results": true,
    "users": [{
        // See (1) for example users
    }, {
        // There will be 10 users in this response, all in the specified organization
    }]
}
  1. Example user in response

Create User

# auth object somes from init_base_auth
auth.create_user(email, email_confirmed=False, send_email_to_confirm_email_address=True,
                 password=None, username=None, first_name=None, last_name=None)
ArgumentDescription
emailThe user’s email address
email_confirmedWhether the email address should start off as already confirmed. If false, the user is required to confirm the email address before they sign in.
send_email_to_confirm_email_addressWhether we should send an email immediately to confirm the user’s email address. If false, the user will get a confirmation email when they first sign in.
passwordOptional password. If unset, the user can set it themselves via their account page
usernameOptional username. Can only be used if username is enabled in your user schema
first_nameOptional first name. Can only be used if name is enabled in your user schema
last_nameOptional last name. Can only be used if name is enabled in your user schema

Successful response:

{
    "user_id": "b5f667fb-e51a-49c6-a396-711e62948689"
}

Migrate User from External Source

A similar function to Create User, but for cases where you already have a user stored in an external system. You can, for example, pass in a password hash for an existing user, and they will be able to login with their same password. It is also possible to provide an existing identifier, and we will store and return it along with our future identifiers, allowing you to link them.

# auth object somes from init_base_auth
auth.migrate_user_from_external_source(email, email_confirmed,
                                       existing_user_id=None, existing_password_hash=None,
                                       existing_mfa_base32_encoded_secret=None,
                                       enabled=None, first_name=None, last_name=None, username=None)
ArgumentDescription
emailThe user’s email address
email_confirmedWhether the email address should start off as already confirmed. If false, the user is required to confirm the email address before they sign in.
existing_user_id(Optional) The user’s ID in the existing system. This ID will be stored on the user as a legacy_user_id and it is present everywhere user_id’s are (e.g. fetching user metadata or validating a user’s token).
existing_password_hash(Optional) The user’s hashed password. We support both bcrypt and argon2 password hashes.
existing_mfa_base32_encoded_secret(Optional) The user’s MFA secret, base32 encoded. If specified the user will have MFA enabled by default.
enabled(Optional) Whether or not the user can login
usernameOptional username. Can only be used if username is enabled in your user schema
first_nameOptional first name. Can only be used if name is enabled in your user schema
last_nameOptional last name. Can only be used if name is enabled in your user schema

Successful response:

{
    "user_id": "b5f667fb-e51a-49c6-a396-711e62948689"
}

Creates a magic link that a user can use to log in. Use this API if you’d prefer to send the magic link to the customer yourself, otherwise, we have Create User which will email them directly.

# auth object somes from init_base_auth
auth.create_magic_link(email, redirect_to_url=None, expires_in_hours=None, create_new_user_if_one_doesnt_exist=None)
ArgumentDescription
emailThe user’s email address
redirect_to_url(Optional) Where to redirect the user after they login. If unspecified, will use the login redirect path specified in your dashboard.
expires_in_hours(Optional) How many hours should the link be valid for?
create_new_user_if_one_doesnt_exist(Optional) If true, this will create a new user if one matching the provided email address doesn’t exist. If false, the request will fail if no user with that email exists. Default is true.

Successful response:

{
    "url": "https://auth.yourdomain.com/..."
}

Update User Email

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.update_user_email(user_id, new_email, require_email_confirmation)
ArgumentDescription
new_emailNew email address for this user
require_email_confirmationWhether the new email address requires confirmation. If true, an email will be sent to the new email address to confirm. If false, the users email address will be updated and confirmed immediately.

Update User Metadata

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.update_user_metadata(user_id, username=None, first_name=None, last_name=None)
ArgumentDescription
usernameOptional username. Can only be used if username is enabled in your user schema
first_nameOptional first name. Can only be used if name is enabled in your user schema
last_nameOptional last name. Can only be used if name is enabled in your user schema

Delete user

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.delete_user(user_id)

Disable user

If successful, the user is logged out and unable to log back in.

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.disable_user(user_id)

Enable user

If successful, the user is able to log back in again.

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.enable_user(user_id)

Fetch an organization

# auth object somes from init_base_auth
org = auth.fetch_org(org_id)

Successful response:

{
    "org_id": "d488996d-8ccc-4101-b5f2-131f5f09ddb6"
    "name": "OneOfYourCustomers"
}

Fetch all organizations

# auth object somes from init_base_auth
auth.fetch_org_by_query(page_size=10, page_number=0, order_by=OrgQueryOrderBy.CREATED_AT_ASC)
ArgumentDescription
page_sizeThe number of results to return at a time. Must be between 1 and 100, default 10.
page_numberUsed to page over results
order_byHow to order the results. See OrgQueryOrderBy for options

Successful response:

{
    "total_orgs": 21,
    "current_page": 0,
    "page_size": 10,
    "has_more_results": true,
    "orgs": [{
        "org_id": "d488996d-8ccc-4101-b5f2-131f5f09ddb6",
        "name": "OneOfYourCustomers"
    }, {
        // There will be 10 orgs in this response
    }]
}

Create Organization

# auth object somes from init_base_auth
auth.create_org(name)
ArgumentDescription
nameThe organization’s name

Successful response:

{
    "org_id": "d488996d-8ccc-4101-b5f2-131f5f09ddb6"
}

Add user to organization

# auth object somes from init_base_auth
auth.add_user_to_org(user_id, org_id, role)
ArgumentDescription
user_idThe user’s ID
org_idThe org’s ID
roleThe role of the user in that organization, e.g. Admin

Successful response:

{}

Delete user

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.delete_user(user_id)

Disable user

If successful, the user is logged out and unable to log back in.

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.disable_user(user_id)

Enable user

If successful, the user is able to log back in again.

# auth object somes from init_base_auth
# Returns true if it was successful, false if the user was not found
auth.enable_user(user_id)