Streamlit in app authentication - st.login

Kaarel Kõrvemaa | Feb 1, 2025 min read

Overview

The new st.user feature will allow any OIDC providers to be added to the app. The biggest challenge have been that keeping the token in the session so that when you rerun the app, you will lose the session. This feature will allow you to avoid this and make the features inside the app more user oriented. Now when desiring apps you can have more security over who sees what and also make the apps public. Allowing to really create public apps that have either subscription based solution, will provide many new business cases to be solved.

Before

This blog looks into Azure solution, but same template can be used in other platforms as well. To be able to create a app like this, you need to be able to create a Entra application, this means that al least Application Administrator or Global Administrator role is needed.

Disclaimer

The feature is not yet in production, API’s might change. Streamlit calls it Nightly releases, which is experimental.

About the auth configuration

The feature has been requested for a long time, there are countless different comments, conversations and packages around the internet. Here is one of the conversation in the github.

The examples that I will show here are from terraform + python combination

All the secrets in streamlit are stored in .streamlit/secrets.toml / or if you have deployed it also in os.env. For the auth to work, you need to create the secrets.toml file. Here is an example how you can create one with terraform.

resource "local_file" "secrets_toml" {
  content  = <<-EOT
  [auth]
  redirect_uri = "https://${azurerm_linux_web_app.app.name}.azurewebsites.net/oauth2callback"
  cookie_secret = "cookie"

  [auth.microsoft]
  redirect_uri = "https://${azurerm_linux_web_app.app.name}.azurewebsites.net/oauth2callback"
  client_id = "${azuread_application.streamlit.client_id}"
  client_secret = "${azuread_application_password.streamlit.value}"
  server_metadata_url = "https://login.microsoftonline.com/${var.ARM_TENANT_ID}/v2.0/.well-known/openid-configuration"
  client_kwargs = { 'prompt' = 'login' }
  EOT
  filename = "${path.module}/app/.streamlit/secrets.toml"
}

if you are missing the file, the error looks like this:

one authentication provide

How to fill the auth information with right values

Most easiest way to create a app like this is from Microsoft Entra portal by creating an app registration. In this example we will create it with terraform.

Here is how you craete it with terraform

First the app registration, this will create the object to Entra. We will ignore the web, because we don’t yet know the web app redirect url, it will come later.

resource "azuread_application" "streamlit" {
  display_name = "streamlit-inapplogin-public"
  owners       = [data.azuread_client_config.current.object_id]

  // add api permissions to read the user, otherwise you will need to grant them yourself in the first login

  lifecycle {
    ignore_changes = [web] // this is for ignoring the redirect values
  }

}

Create a service principal

resource "azuread_service_principal" "streamlit" {
  client_id                    = azuread_application.streamlit.client_id
  app_role_assignment_required = false
  owners                       = [data.azuread_client_config.current.object_id]
}

Rotating the secret


resource "time_rotating" "example" {
  rotation_days = 180
}

Now the secret for the app


resource "azuread_service_principal_password" "streamlit" {
  service_principal_id = azuread_service_principal.streamlit.id
  rotate_when_changed = {
    rotation = time_rotating.example.id
  }
}

Adding the secret to the application

resource "azuread_application_password" "streamlit" {

  application_id = azuread_application.streamlit.id

}

Optional

You can also write all the secrets to key vault

resource "azurerm_key_vault_secret" "key" {
  name         = "${azuread_application.streamlit.display_name}-client-id"
  value        = azuread_application.streamlit.client_id
  key_vault_id = azurerm_key_vault.kv.id

}
resource "azurerm_key_vault_secret" "key-secret" {
  name         = "${azuread_application.streamlit.display_name}-client-secret"
  value        = azuread_application_password.streamlit.value
  key_vault_id = azurerm_key_vault.kv.id

}

Streamlit app with authentication requirements

At the time of writing this the version 1.42 is not live. Which means that in the requirements file you need to use streamlit-nightly

in the requirements.txt you need these values:

  • streamlit-nightly
  • Authlib

Authlib is a OpenID and Auth library that enables the authentication.

Streamlit code.

import streamlit as st

microsoft_login = st.button("Microsoft Entra login")

if microsoft_login:
    st.login(provider="microsoft")


logout_button = st.button("Logout")

if logout_button:
    st.logout()

The new api is st.login(provider=“microsoft”) and this will map with the [auth.microsoft] and find the right provider. I have also tested, that you can have many providers in the same app.

Getting the meta data about the user. There are two options for that, one is using the st.experimental_user or you can get the information from the st.context.headers.


user_data = st.experimental_user 
# Check if user data exists and get the name
if user_data and user_data.get("is_logged_in", False):
    if "name" in user_data and user_data["name"]:
        st.write(f"User name: {user_data['name']}")
    if "email" in user_data and user_data["email"]:
        st.write(f"Email: {user_data['email']}")
    if "preferred_username" in user_data and user_data["preferred_username"]:
        st.write(f"Preferred Username: {user_data['preferred_username']}")

Azure webapp

You don’t make any special configuration for the web app! It will be a publicly available web app. If you are looking to have authentication in the fron of the app, with only logged in users can access, you can check my other blog about it.

You need a service plan before you can deploy a web app

resource "azurerm_linux_web_app" "app" {
  depends_on          = [azuread_application.streamlit, azurerm_service_plan.streamlit]
  name                = "webapp-st-${var.project}"
  location            = var.location
  service_plan_id     = azurerm_service_plan.streamlit.id
  resource_group_name = data.azurerm_resource_group.main.name
  https_only          = true

  identity {
    type = "SystemAssigned"
  }

  site_config {
    app_command_line = "python -m streamlit run app.py --server.port 8000 --server.address 0.0.0.0"

    application_stack {
      python_version = "3.10"
    }
  }

  app_settings = {
    "SCM_DO_BUILD_DURING_DEPLOYMENT" = true

    // Add other app settings as needed
  }
}

Creating the secrets.toml file with “Terraform”

Deploying application with Terraform is a try thing and needs some extra work. In production, separate the app development and the infra into different deployment pipelines. Here is an example how you can crearte it and deploy it with terraform and azure cli.

// this will create the secrets_toml file, the current app requires it, can't be in the os environment
resource "local_file" "secrets_toml" {
  content  = <<-EOT
  [auth]
  redirect_uri = "https://${azurerm_linux_web_app.app.name}.azurewebsites.net/oauth2callback"
  cookie_secret = "cookie"

  [auth.microsoft]
  redirect_uri = "https://${azurerm_linux_web_app.app.name}.azurewebsites.net/oauth2callback"
  client_id = "${azuread_application.streamlit.client_id}"
  client_secret = "${azuread_application_password.streamlit.value}"
  server_metadata_url = "https://login.microsoftonline.com/${var.ARM_TENANT_ID}/v2.0/.well-known/openid-configuration"
  client_kwargs = { 'prompt' = 'login' }
  EOT
  filename = "${path.module}/app/.streamlit/secrets.toml"
}



data "archive_file" "app" {
  depends_on = [ local_file.secrets_toml]
  type        = "zip"
  source_dir  = "./app"
  output_path = var.archive_file_streamlit

}

// This local block defines a command for publishing code to the Azure Web App (Linux).
locals {
  publish_code_command_linux = "az webapp deploy --resource-group ${azurerm_linux_web_app.app.resource_group_name} --name ${azurerm_linux_web_app.app.name} --src-path ${var.archive_file_streamlit}"
}

// This null_resource block publishes code to the Azure Web App (Linux) using the local-exec provisioner.
resource "null_resource" "app" {
  depends_on = [data.archive_file.app, local_file.secrets_toml]
  provisioner "local-exec" {
    command = local.publish_code_command_linux
  }
  
  triggers = {
    input_json           = filemd5(var.archive_file_streamlit)
    publish_code_command = local.publish_code_command_linux
  }
}

When using this code, there needs to be a empty ./streamlit.zip file in the root. null_resource resource can sometimes be very hard. The issue here is that local_file.secrets_toml needs to be created before the archive_file is created and null_resource can’t be run without the streamlit.zip existing.

Conclusion

In-app authentication with Streamlit is a powerful feature that can bring enhanced security and user management to your applications. By leveraging Microsoft’s Entra for authentication and using Terraform for infrastructure as code, you can seamlessly integrate secure login capabilities into your Streamlit apps. Although the feature is still in the experimental phase, it promises to open up new business opportunities by allowing for secure, user-specific, and potentially subscription-based functionality. Stay tuned for more updates as the feature moves into production, and start experimenting with it today to make your Streamlit apps more robust and user-friendly.

Full code can be found from here

Extra

If your app doesn’t start to work and shows error that there is a problem with loading the app.

Error like this

:( Application Error
If you are the application administrator, you can access the diagnostic resources.

Go to Azure web app in Azure portal

Settings -> configurations -> App inline commands remove the start command

python -m streamlit run app.py --server.port 8000 --server.address 0.0.0.0

Save and then add it again, should solve the problem. There is a web app bug.