Different authentication mechanisms to secure your atoti session

Leverage your existing authentication provider with Atoti+

This article is part of a series that explains how we can implement secured and managed access in atoti through the Atoti+ plugin. In this article, we introduce various supported authentication mechanisms in atoti such as: 

atoti also supports Kerberos even though we are not covering it in this article. 

Basic authentication

Let’s start with the most straightforward type of implementation that we can have – basic authentication. All it takes is to:

  • Instantiate session to use basic authentication
  • Create atoti user
  • Assign roles if applicable 

1 – Instantiate session to use basic authentication

session = tt.Session(authentication=tt.BasicAuthenticationConfig(realm="atoti Realm"))

2 – create atoti user 

session.security.basic.create_user("atoti_admin", password="password")

3 – assign roles if applicable

session.security.individual_roles["atoti_admin"].add("ROLE_ADMIN")

Users have to be assigned the role “ROLE_USER” before they can access the web application. However, users are granted this role upon creation in the basic authentication mechanism.

 

 

Authentication mechanisms - Authenticating users using Basic authentication
The user is prompted username and password when trying to access a shared dashboard directly via its URL

 

We can always maintain a file for the user credentials and use other Python libraries such as watchdog to monitor the file for modification. Then, we can automate the creation, modification and deletion of users:

from watchdog.observers.polling import PollingObserver
from watchdog.events import FileSystemEventHandler, FileCreatedEvent

class AtotiWatcher(FileSystemEventHandler):
    def on_modified(self, event: FileCreatedEvent):
        try:
            users_df = pd.read_csv(event.src_path)
            
            for row in users_df.to_dict(orient="records"):
                if (row["User"] not in session.security.basic.users) & (row["Status"] == "Active"): 
                    user = session.security.basic.create_user(row["User"], password=row["Password"])
                    print(f"Create user: {user} - role(s): {user.roles}")
                else:
                    if row["Status"] == "Inactive":
                        session.security.basic.users.pop(row["User"])
                    else:
                        session.security.basic.users[row["User"]].password = row["Password"]
                    print(f"Update user password: {user}")
                
        except Exception as error:
            print(error)


observer = PollingObserver()
observer.schedule(AtotiWatcher(), "users")
observer.start()

Lightweight Directory Access Protocol (LDAP)

LDAP is a widely used protocol for directory services authentication. Other than authentication, we use LDAP to manage users’ roles as well. Therefore, similar to basic authentication, we configure the LDAP authentication when we instantiate the session. However, we will:

  • not create the users within atoti
  • create atoti roles that require restrictions
  • map LDAP roles to atoti roles

We will ignore roles with restrictions in this article and focus on the authentication configuration, as well as the role mapping to ensure we can test our user login. 

1 – Instantiate session to use LDAP

authentication = tt.LdapConfig(
    url="ldap://localhost:10389",
    base_dn="dc=example,dc=com",
    user_search_base="ou=people",
    group_search_base="ou=roles",
)

session = tt.Session(authentication=authentication, user_content_storage="./content")

The image below shows how we configure the various parameters against the LDAP server. In our use case, we used Apache DS to set up a local LDAP instance.

 

 

2 – Map roles between LDAP and atoti 

Again, remember we have to assign users the atoti role “ROLE_USER” in order to access the web application? We can grant everyone this access programmatically as follows:

session.security.ldap.default_roles.update(["ROLE_USER"])

This means that anyone who gets authenticated against the LDAP will have access to the web application. Alternatively, we can grant authorized users an LDAP role corresponding to “ROLE_USER” in LDAP, e.g. “ROLE_ATOTI_USER” in the image above. 

In this case, we perform a role mapping between the roles in LDAP against the roles in atoti:

session.security.ldap.role_mapping.update(
    {
        # LDAP role: atoti role
        "ROLE_ATOTI_ADMIN": ["ROLE_ADMIN"],
        "ROLE_ATOTI_USER": ["ROLE_USER"],
        "ROLE_ATOTI_SHARE": ["ROLE_SHARE"],
    }
)

It is important to note that in this mapping, the LDAP roles have to be capitalized.

 

Authentication mechanisms - Authenticating users using LDAP
Similar to Basic Authentication, users perform login with the default browser sign-in prompt.

OpenID Connect (OIDC)

OpenID connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. This allows users to sign-in to multiple websites using an existing account with an authorization server, and obtain login and profile information about the user.

Some of the OpenID Connect authentication providers include Auth0, Google and Keycloak.

We haven’t tested the authentication providers exhaustively but in this article, we present two different examples with Auth0 and Google.

Google Cloud

As explained in the Google Cloud documentation, an application initiates an OAuth consent flow. After the user completes the flow, the application receives an access token that enables your application to call Google Cloud APIs on behalf of the user.

 

 

In this case, atoti uses Google API only to authenticate the user but not the roles. Therefore, for OIDC via Google authentication, we will:

  • Instantiate session to use Google 
  • Assign atoti roles to user  

1 – Instantiate session to use Google

authentication = tt.OidcConfig(
    provider_id="google",
    issuer_url="https://accounts.google.com",
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    scopes=["https://www.googleapis.com/auth/userinfo.email"],
    name_claim="email",
)

session = tt.Session(
    port=10011, authentication=authentication
)

From the code snippet above, we can see that we assigned a port to the session. We need it to configure our authorized redirect URIs in Google Cloud, not to mention the firewall setup for the application.

 

 

Other than the application URL, one of the redirect URIs has to be f"{session_link()}/login/oauth2/code/{provider_id}" where session_link() returns the application URL and provider_id is google in this case.

The below image shows where we can obtain the various configurations from the Google Cloud console.

 

2 – Assign atoti roles to user

We could assign “ROLE_USER” to users by default, ONLY IF the user type for the app registered under the OAuth consent screen is Internal. Otherwise, anyone who is authenticated in Google (including those outside of your organization) will gain access to the application.

 

 

Therefore, it is perhaps a safer design to assign users the user role individually.

session.security.oidc.role_mapping.update(
    {
        "inspire_m@test.com": ["ROLE_USER","ROLE_USER"]
    }
)
Authentication mechanisms - Authenticating users using OIDC config to connect to Google Cloud API
Instead of a browser prompt, we get the standard Google consent screen for login.

Auth0

Okta and Auth0 provide a Customer Identity (CIAM) solution which also allows us to manage user roles. I recommend you to read the article Guide to Auth0 setup for Security implementation with Atoti+.   

Considering we use Auth0 to manage user roles, the steps we take to set up authentication are similar to LDAP:

  • do not create the users in atoti
  • create atoti roles that require restrictions
  • map Auth0 roles to atoti roles

Let’s zoom in on the authentication configuration and skip the role mapping since we have covered that earlier.

1 – Instantiate session to use Auth0

authentication = tt.OidcConfig(
    provider_id="auth0",
    issuer_url="https://dev-5m2svhd0.us.auth0.com/",
    client_id=os.environ["AUTH0_CLIENT_ID"],
    client_secret=os.environ["AUTH0_CLIENT_SECRET"],
    name_claim="name",
    scopes=["email", "profile", "username", "roles"],
    roles_claims=[
        "https://dev-5m2svhd0:us:auth0:com/roles",
    ],
)

session = tt.Session(
    port=10011, authentication=authentication
)

Again, we fixed the port for the same reasons.

 

 

So, the provider_id for the callback URL is now auth0 as highlighted above.

The image below shows where we can obtain the various configurations from the Auth0 console.

 

 

The parameter “role_claim” gives the claims of the ID token from which to extract roles to use as keys in the role_mapping. From the user profile, we can find the “role_claim” value, which is a result of the add-role-to-id-token action. 

On the other hand, the parameter “name_claim” is the name of the claim in the ID token to use as the name of the user. When we set the “name_claim” to “email”, atoti will display the email address of the user who is logged in. However, since user’s name is also available in the user profile, we can switch the “name_claim” to “name” and display the user’s name instead as shown below.

 

Displayed name in the web application depends on the property set in "name_claim"

 

We can proceed to test our login once we finished mapping the roles and have granted users the role “ROLE_USER”.

 

Authentication mechanisms - Authenticating users using OIDC config to connect to Auth0
We can also use Google authentication with Auth0.

What’s next…?

Check out the notebook gallery to see the examples listed above. Generally, the steps are:

  1. Instantiate the session with the relevant authentication mechanism, along with a fixed port recommended
  2. Create users if a basic authentication is used
  3. Create roles in atoti if there are any with restrictions
  4. Assign roles to users directly if user’s roles are not returned by the authentication provider
  5. Map roles from authentication provider to roles in atoti if it’s available.  

Now that we have seen how easy it is to set up authentication in atoti with the Atoti+ plugin, we will explore access management next. Stay tuned!

Latest posts

Contributing to atoti notebook gallery
Share your atoti use case with our community If you haven’t heard...
atoti: Guide to implementing lookup in a multidimensional cube
A simple example of converting your analytics into any referenced currency Today...
Integrating an External Portfolio Optimizer with atoti
Given a portfolio and an optimizer, can we find a ‘better’ portfolio...

Join our Community

    Like this post ? Please share

    Documentation
    Information
    Follow Us

    atoti Free Community Edition is developed and brought to you by ActiveViam. Learn more about ActiveViam at activeviam.com.