Authentication#
The Symphony BDK authentication API allows developers to authenticate their bots and apps using RSA authentication mode.
The following sections will explain you:
how to authenticate your bot service account
how to authenticate your app to use OBO (On Behalf Of) authentication
Bot authentication#
In this section we will see how to authenticate a bot service account. You will notice that everything has to be done
through your BDK config.yaml
, making your code completely agnostic to authentication modes (RSA or certificate).
Only one of certificate or RSA authentication should be configured in one BDK config.yaml
. If both of them are
provided, an AuthInitializationError
will be thrown when you try to authenticate to the bot service account.
Bot authentication using RSA#
In this section we will see how to authenticate a bot service account using RSA authentication.
Read more about RSA authentication here
Required config.yaml
setup:
host: acme.symphony.com
bot:
username: bot-username
privateKey:
path: /path/to/rsa/private-key.pem
Bot authentication using Client Certificate#
Read more about Client Certificate authentication here
Required config.yaml
setup:
host: acme.symphony.com
bot:
username: bot-username
certificate:
path: /path/to/certificate.pem
To know more about the format of the certificate file, check SSLContext.load_cert_chain.
The certificate path will be passed to the certfile
parameter of the load_cert_chain_method
. We do not pass anything
to keyfile
and password
parameters, which means certificate and decrypted private key should be put in the same file.
The certificate path should lead to a single file in PEM format containing the certificate and the decrypted private key. We do not support password encrypted private keys.
Alternatively, if you have another key format, you can use the OpenSSL Command line tool to convert it to .pem
format:
openssl pkcs12 -in certificate.{p12, pfx} -out certificate.pem -nodes
Bot authentication deep-dive#
The code snippet below explains how to manually retrieve your bot authentication session. However, note that by default
those operations are done behind the scene through the SymphonyBdk
entry point.
import logging
from symphony.bdk.core.config.loader import BdkConfigLoader
from symphony.bdk.core.symphony_bdk import SymphonyBdk
async def run():
config = BdkConfigLoader.load_from_symphony_dir("config.yaml")
async with SymphonyBdk(config) as bdk:
auth_session = bdk.bot_session()
logging.info(await auth_session.key_manager_token)
logging.info(await auth_session.session_token)
Authentication using private key content#
Instead of configuring the path of RSA private key config file, you can also authenticate the bot and extension app by using directly the private key or certificate content. This feature is useful when either RSA private key is fetched from an external secrets storage. The code snippet below will give you an example showing how to set directly the private key content to the Bdk configuration for authenticating the bot.
import asyncio
import logging
from symphony.bdk.core.config.loader import BdkConfigLoader
from symphony.bdk.core.symphony_bdk import SymphonyBdk
async def run():
# loading configuration
config = BdkConfigLoader.load_from_symphony_dir("config.yaml")
# update private key with content
private_key_string = '-----BEGIN RSA PRIVATE KEY-----'
config.bot.private_key.content = private_key_string
async with SymphonyBdk(config) as bdk:
auth_session = bdk.bot_session()
logging.info(await auth_session.key_manager_token)
logging.info(await auth_session.session_token)
Multiple bot instances#
By design, the SymphonyBdk
object contains a single bot session. However, you might want to create an application that
has to handle multiple bot sessions, potentially using different authentication modes. This is possible by creating
multiple instances of SymphonyBdk
using different configurations:
from symphony.bdk.core.config.loader import BdkConfigLoader
from symphony.bdk.core.symphony_bdk import SymphonyBdk
async def run():
config_a = BdkConfigLoader.load_from_symphony_dir("config_a.yaml")
config_b = BdkConfigLoader.load_from_symphony_dir("config_b.yaml")
async with SymphonyBdk(config_a) as bdk_a, SymphonyBdk(config_b) as bdk_b:
# use your two service accounts
App authentication#
Application authentication is completely optional but remains required if you want to use OBO.
Using RSA#
Required config.yaml
setup:
host: acme.symphony.com
app:
appId: app-id
privateKey:
path: /path/to/rsa/private-key.pem
Using Client Certificate#
Read more about certificate authentication here
Required config.yaml
setup:
host: acme.symphony.com
app:
appId: app-id
certificate:
path: /path/to/certificate.pem
To know more about the format of the certificate file, check SSLContext.load_cert_chain.
The certificate path will be passed to the certfile
parameter of the load_cert_chain_method
. We do not pass anything
to keyfile
and password
parameters, which means certificate and decrypted private key should be put in the same file.
Circle Of Trust#
Read more about Circle Of Trust here
from symphony.bdk.core.config.loader import BdkConfigLoader
from symphony.bdk.core.symphony_bdk import SymphonyBdk
async def run():
config = BdkConfigLoader.load_from_symphony_dir("config.yaml")
async with SymphonyBdk(config) as bdk:
ext_app_authenticator = bdk.app_authenticator()
app_auth = await ext_app_authenticator.authenticate_extension_app("appToken")
ta = app_auth.app_token
ts = app_auth.symphony_token
assert await ext_app_authenticator.is_token_pair_valid(ta, ts)
In order to have a fully working extension app, your extension app backend will have to expose the following endpoints:
a POST authentication endpoint (e.g. POST /authenticate) which will we generate an app token, call
await ext_app_authenticator.authenticate_extension_app("appToken")
and return the result.a POST validate tokens endpoint (e.g. POST /tokens) which will validate the
appToken
andsymphonyToken
passed in the body is valid according to the result ofawait ext_app_authenticator.is_token_pair_valid(app_token, symphony_token)
.a POST validate jwt endpoint (e.g. POST /jwt) which will validate a jwt passed in the
jwt
field of the body and will return the result ofawait ext_app_authenticator.validate_jwt(jwt)
.
OBO (On Behalf Of) authentication#
Read more about OBO authentication here
The following example shows how to retrieve OBO sessions using username
(type str
) or user_id
(type int
)
and to call services which have OBO endpoints (users, streams, connections and messages so far):
from symphony.bdk.core.config.loader import BdkConfigLoader
from symphony.bdk.core.symphony_bdk import SymphonyBdk
async def run():
config = BdkConfigLoader.load_from_symphony_dir("config.yaml")
async with SymphonyBdk(config) as bdk:
obo_auth_session = bdk.obo(username="username")
async with bdk.obo_services(obo_auth_session) as obo_services:
obo_services.messages().send_message("stream_id", "<messageML>Hello on behalf of user!</messageML>")
BDK running without Bot username (service account) configured#
When the bot username
(service account) is not configured in the Bdk configuration, the bot project will be still
runnable but only in the OBO mode if the app authentication is well-configured.
The config.yaml
requires at least the application configuration:
host: acme.symphony.com
app:
appId: app-id
privateKey:
path: /path/to/private-key.pem
If users still try to access to Bdk services directly from SymphonyBdk
facade object, a BotNotConfiguredError
will be thrown.
The example in part above shows how a bot project works without bot username
configured.