11 KiB
Configure Chill for calendar and absence synchronisation and SSO with Microsoft Graph (Outlook)
Chill offers the possibility to:
- authenticate users using Microsoft Graph, with relatively small adaptations;
- synchronize calendar in both ways (see the user manual for a large description of the feature ).
Both can be configured separately (synchronising calendars without SSO, or SSO without a calendar).
Please note that the user's email address is the key to associate Chill's users with Microsoft's ones.
Configure SSO
On the Azure side
Configure an app with the Azure interface and give it the name of your choice.
Grab the tenant's ID for your app, which is visible on the main tab "Vue d'ensemble":
This is the variable which will be named SAML_IDP_APP_UUID.
Go to the "Single sign-on" ("Authentication unique") section. Choose "SAML" as a protocol and fill those values:
- The
entityIdseems to be arbitrary. This will be your variableSAML_ENTITY_ID; - The url response must be your Chill's URL appended by
/saml/acs - The only used attributes is
emailaddress, which must match the user's email one.
You must download the certificate, as base64. The format for the download is cer: you will remove the first and last line (the ones with -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----), and remove all the return line. The final result should be something as MIIAbcdef...XyZA=.
This certificat will be your SAML_IDP_X509_CERT variable.
The url login will be filled automatically with your tenant id.
Remember to provider user's access to your app, using the "Utilisateurs et groupes" tab:
You must now have gathered all the required variables for SSO:
SAML_BASE_URL=https://test.chill.be # must be SAML_ENTITY_ID=https://test.chill.be # must match the one entered SAML_IDP_APP_UUID=42XXXXXX-xxxx-xxxx-xxxx-xxxxxxxxxxxx SAML_IDP_X509_CERT: MIIC...E8u3bk # truncated
Configure chill app
- add the bundle
hslavich/oneloginsaml-bundle - add the configuration file (see example above)
- configure the security part (see example above)
- add a user SAML factory into your src, and register it
# config/packages/hslavich_onelogin.yaml
parameters:
saml_base_url: '%env(resolve:SAML_BASE_URL)%'
saml_entity_id: '%env(resolve:SAML_ENTITY_ID)%'
saml_idp_x509cert: '%env(resolve:SAML_IDP_X509_CERT)%'
saml_idp_app_uuid: '%env(resolve:SAML_IDP_APP_UUID)%'
hslavich_onelogin_saml:
# Basic settings
idp:
entityId: 'https://sts.windows.net/%saml_idp_app_uuid%/'
singleSignOnService:
url: 'https://login.microsoftonline.com/%saml_idp_app_uuid%/saml2'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
singleLogoutService:
url: 'https://login.microsoftonline.com/%saml_idp_app_uuid%/saml2'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
x509cert: '%saml_idp_x509cert%'
sp:
entityId: '%saml_entity_id%'
assertionConsumerService:
url: '%saml_base_url%/saml/acs'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
singleLogoutService:
url: '%saml_base_url%/saml/'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
privateKey: ''
# Optional settings.
baseurl: '%saml_base_url%/saml'
strict: true
debug: true
security:
nameIdEncrypted: false
authnRequestsSigned: false
logoutRequestSigned: false
logoutResponseSigned: false
wantMessagesSigned: false
wantAssertionsSigned: false
wantNameIdEncrypted: false
requestedAuthnContext: true
signMetadata: false
wantXMLValidation: true
signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256'
contactPerson:
technical:
givenName: 'Tech User'
emailAddress: 'techuser@example.com'
support:
givenName: 'Support User'
emailAddress: 'supportuser@example.com'
organization:
en:
name: 'Example'
displayname: 'Example'
url: 'http://example.com'
# config/security.yaml
# merge this with other existing configurations
security:
providers:
saml_provider:
# Loads user from user repository
entity:
class: Chill\MainBundle\Entity\User
property: username
firewalls:
default:
# saml part:
saml:
username_attribute: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
# weird behaviour in dev environment... configuration seems different
# username_attribute: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
# Use the attribute's friendlyName instead of the name
use_attribute_friendly_name: false
user_factory: user_from_saml_factory
persist_user: true
check_path: saml_acs
login_path: saml_login
logout:
path: /saml/logout
// src/Security/SamlFactory.php
namespace App\Security;
use Chill\MainBundle\Entity\User;
use Hslavich\OneloginSamlBundle\Security\Authentication\Token\SamlTokenInterface;
use Hslavich\OneloginSamlBundle\Security\User\SamlUserFactoryInterface;
class UserSamlFactory implements SamlUserFactoryInterface
{
public function createUser(SamlTokenInterface $token)
{
$attributes = $token->getAttributes();
$user = new User();
$user->setUsername($attributes['http://schemas.microsoft.com/identity/claims/displayname'][0]);
$user->setLabel($attributes['http://schemas.microsoft.com/identity/claims/displayname'][0]);
$user->setPassword('');
$user->setEmail($attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'][0]);
$user->setAttributes($attributes);
return $user;
}
}
Configure sync and calendar access
The purpose of this configuration is the following:
- let user read their calendar and shared calendar within Chill (with the same permissions as the one configured in Outlook / Azure);
- allow chill instance to write appointment ("Rendez-vous") into their calendar and invite other users to their appointment;
- allow chill instance to be notified if an appointment is added or removed by the user within another interface than Chill: if the appointment matches another one created in the Chill interface, the date and time are updated in Chill;
- allow chill instance to read the absence of the user and, if set, mark the user as absent in Chill;
The sync process might be configured in the same app, or into a different app on the Azure side.
The synchronization processes use Oauth 2.0 / OpenID Connect for authentication and authorization.
Two flows are in use:
-
we authenticate "on behalf of a user", to allow users to see their own calendar or another user's calendar into the web interface.
Typically, when the page is loaded, Chill first checks that an authorization token exists. If not, the user is redirected to Microsoft Azure for authentification, and a new token is grabbed (most of the times, this is transparent for users).
-
Chill also acts "as a machine", to synchronize calendars with a daemon background.
One can access the configuration using this screen (it is quite well hidden into the multiple of tabs):
You can find the oauth configuration on the "Securité > Autorisations" tab and click on "application registration" (not translated).
Add a redirection URI for your authentification:
The URI must be "your chill public url" with /connect/azure/check at the end.
Allow some authorizations for your app:
Take care of the separation between authorization "on behalf of a user" (déléguée), or "for a machine" (application).
Some explanation:
- Users must be allowed to read their user profile (
User.Read), and the profile of other users (User.ReadBasicAll); - They must be allowed to read their calendar (
Calendars.Read), and the calendars shared with them (Calendars.Read.Shared);
The sync daemon must have write access:
- the daemon must be allowed to read all users and their profile, to establish a link between them and the Chill's users: (
Users.Read.All); - it must also be allowed to read and write into the calendars (
Calendars.ReadWrite.All); - for sending invitation to other users, the permission (
Mail.Send) must be granted; - and, for reading the absence status of the user and sync it with chill, it must be able to read the mailboxSettings (
MailboxSettings.Read).
At this step, you might choose to accept those permissions for all users or let them do it by yourself.
Grab your client id:
This will be your OAUTH_AZURE_CLIENT_ID variable.
Generate a secret:
This will be your OAUTH_AZURE_CLIENT_SECRET variable.
And get you azure's tenant id, which is the same as the SAML_IDP_APP_UUID (see above).
Your variables will be:
OAUTH_AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx OAUTH_AZURE_CLIENT_TENANT=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx OAUTH_AZURE_CLIENT_SECRET: 3-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Then, configure chill:
Enable the calendar sync with Microsoft Azure:
# config/packages/chill_calendar.yaml
chill_calendar:
remote_calendars_sync:
microsoft_graph:
enabled: true
and configure the oauth client:
# config/packages/knp_oauth2_client.yaml
knpu_oauth2_client:
clients:
azure:
type: azure
client_id: '%env(OAUTH_AZURE_CLIENT_ID)%'
client_secret: '%env(OAUTH_AZURE_CLIENT_SECRET)%'
redirect_route: chill_calendar_remote_connect_azure_check
redirect_params: { }
tenant: '%env(OAUTH_AZURE_CLIENT_TENANT)%'
url_api: 'https://graph.microsoft.com/'
default_end_point_version: '2.0'
You can now process for the first api authorization on the application side, (unless you did it in the Azure interface) and get the first token by using :
bin/console chill:calendar:msgraph-grant-admin-consent
This will generate an url that you can use to grant your app for your tenant. The redirection may fail in the browser, but this is not relevant: if you get an authorization token in the CLI, the authentication works.
Run the processes to synchronize
The calendar synchronization is processed using symfony messenger. It seems to be interesting to configure a queue (in the postgresql database it is the simplest way), and to run a worker for synchronization, at least in production.
This cli command does the association between chill's users and Microsoft's users:
bin/console chill:calendar:msgraph-user-map-subscribe
This command:
- will associate the Microsoft's user metadata in our database;
- and, most important, create a subscription to get notification when the user alters his calendar, to sync chill's event and ranges in sync.
The subscription is at least at most 3 days. This command should be run:
- at least each time a user is added;
- and, at least, every three days.
In production, we advise running it at least every day to get the sync working.