Authorization and Authentication

Learn how to use the SDK to build integrations with different authorization styles.

Authentication is a crucial aspect of Lumos Integration connectors, ensuring secure and authorized access to third-party APIs. This section outlines the recommended authentication approach used in Lumos connectors.

Recommended Principles

  1. OAuth2 Preference: OAuth2 is the preferred authentication method for Lumos connectors due to its security and flexibility.
  2. Credential Readiness: Connectors expect authentication credentials to be ready for use, the connector SDK has a fully fledged OAuth module, which handles specific authorization capabilities.
  3. Standardized Implementation: Authentication is implemented inside the client.py during it’s initialization and the SDK offers pre-defined models for the most common authentication schemes.

OAuth2 Authentication Flow

The general process is as follows:

  1. The user initiates the OAuth flow with the third-party service, using a Connect button in Lumos.
  2. After successful authentication, the service provides an access token.
  3. This access token, along with any other required credentials, is saved and later on passed to the Lumos connector.
  4. The connector uses these credentials for all subsequent API requests.

Implementing Authentication

Client class

Each connector typically implements a client class (extending the BaseIntegrationClient SDK class) in a dedicated client.py file:

# Example: abc/client.py
import httpx
from connector.oai.base_clients import BaseIntegrationClient
from connector.oai.capability import Request, get_oauth
from connector.utils.httpx_auth import BearerAuth

BASE_URL = 'https://api.abc.com'

class AbcClient(BaseIntegrationClient):
    requires_response_body = True

    @classmethod
    def prepare_client_args(cls, args: Request) -> dict[str, t.Any]:
        return {
            "auth": BearerAuth(token=get_oauth(args).access_token),
            "base_url": BASE_URL,
        }
    
    # Implement your API calls / client methods below

The BaseIntegrationClient class serves as the minimal abstraction layer for writing a httpx AsyncClient implementation. The prepare_client_args class method then provides arguments for httpx to initialize.

Integration Configuration

In the integration.py file, the authentication type and other connector-specific settings are specified in the Integration object. This configuration can be more complex than when just using a standard OAuth credential. The following is an example when using OAuth2 Authorziation Code Flow:

# Example: abc/Integration.py
from abc.settings import AbcSettings
from connector.generated import OauthCredential
from connector.oai.modules.oauth_module_types import OAuthSettings

integration = Integration(
    app_id="abc",
    settings_model=AbcSettings,
    auth=OAuthCredential,
    oauth_settings=OAuthSettings(
        authorization_url="https://abc/oauth/authorize",
        token_url="https://abc/oauth/token",
    ),
)

This example assumes that Lumos can and will acquire a public OAuth application that users can authorize with and provide consent to impersonate the logged in user.

Custom Authentication Models

Connectors often require custom authentication models that extend the basic credential types provided by Lumos. These are typically defined in <connector>/settings.py.

Token Based Authentication
# Example: abc/settings.py
import pydantic
from connector.generated import TokenCredential
from connector.serializers.request import AnnotatedField

class abcTokenAuth(TokenCredential):
    token: str = AnnotatedField(
        title="API Token",
        description=(
            "To create an API Token, navigate to abc dashboard"
        ),
        secret=True,
    )

This custom model:

  • ExtendsΒ TokenCredentialΒ for token-based authentication.
  • UsesΒ AnnotatedFieldΒ to ensure secure handling of the token, using the secret argument.
Basic Authentication
# Example: abc/settings.py
import pydantic
from connector.generated import BasicCredential
from connector.serializers.request import AnnotatedField

class abcBasicAuth(BasicCredential):
    username: str = AnnotatedField(
        title="Username",
        description=(
            "Your username"
        ),
    )
    password: str = AnnotatedField(
		    title="Password",
		    description=(
				    "Your password"
		    ),
		    secret=True,
    )

This custom model:

  • ExtendsΒ BasicCredentialΒ for basic authentication.
  • UsesΒ AnnotatedFieldΒ to ensure secure handling of the password, using the secret argument.
OAuth Client Credentials Flow

Sometimes called machine to machine flow, this OAuth flow is widely used for server-side communication. It requires the caller to authorize and obtain an access_token in a single request-response flow.

For the Client Credentials Flow authentication, you might extend the OAuthClientCredential model. Here's an example:

# Example: abc/settings.py
from connector.generated import OAuthClientCredential
from connector.serializers.request import AnnotatedField
from pydantic import Field

class AbcOAuthClientCredentials(OAuthClientCredential):
    scopes: list[str] = AnnotatedField(title="Scopes", hidden=True)
    access_token: str = AnnotatedField(title="Access Token", hidden=True)
    client_id: str = Field(
        title="Client ID",
        description="Specific instructions to obtain a Client ID.",
    )

    client_secret: str = SecretField(
        title="Client Secret",
        description="Specific instructions to obtain a Client Secret",
    )
    
# Example: abc/integration.py
from abc.settings import AbcAuth, AbcSettings
from connector.oai.modules.oauth_module_types import OAuthCapabilities, OAuthFlowType, OAuthSettings

integration = Integration(
    app_id="abc",
    settings_model=AbcSettings,
    auth=AbcAuth,
    oauth_settings=OAuthSettings(
		    capabilities=OAuthCapabilities(
				    # Client Credentials Flow often does not support refreshing
				    # You should disable the capability of this module if that is the case
            refresh_access_token=False,
        ),
        token_url="https://abc.com/oauth/token",
        flow_type=OAuthFlowType.CLIENT_CREDENTIALS,
        scopes={
            StandardCapabilityName.LIST_ACCOUNTS: "some required scopes separated with space",
            # ...
        },
    ),
)

This example shows how you can extend the standard OAuthClientCredential model and use the SDK provided OAuth module.

Best Practices

  1. Security: Use appropriate field types (e.g.,Β AnnotatedField with respective arguments like hidden (from end user, for example when not needed), and secret (sensitive secret)) for sensitive information.
  2. Extensibility: Design your authentication and settings models to be easily extensible as API requirements change.
  3. Validation: Leverage Pydantic's validation capabilities to ensure that provided credentials and settings meet the required format and constraints. The SDK has many different utilities that assist with this, like get_oauth, get_token, etc.

Connector Specific Considerations

While the general structure remains consistent, each connector may have unique authentication requirements.

Always refer to the specific API documentation of the service you're integrating with for the most accurate authentication requirements.

If you require multiple authentication credentials, it is recommended to append the extra credentials to the Settings object using appropriate secret arguments.