Configuring tine ® for Single Sign On (SSO)
tine ® as Single Sign On Identity Provider (SSO IdP)
tine ® can act as SSO identity provider for oidc and SAML2
1) Install SSO application
- Go to
setup.phpand make sure SSO is installed - In the UI go to
Admin>Applications>SSOand make sure SSO is activated - Installing SSO will automatically generate the necessary certificates
- SSO will automatically rotate the certificates every 6 months
2) Generate keys manually
call the tine ® cli: tine20.php --method=SSO.generateKey
To check if you have valid certificates, you can call these URLs: * https://my.tine.url/sso/saml2/idpmetadata * https://my.tine.url/sso/oauth2/certs
3) Create config
<?php
return [
'SSO' => [
'pwdLessLogin' => 'both',
'oauth2' => [
'enabled' => true,
],
'saml2' => [
'enabled' => true,
'tineLogout' => true,
],
],
];
4) Clear config cache
#! /bin/sh
docker-compose exec --user tine20 web sh -c "cd /usr/share/tine20/ && php setup.php --config=\$TINE20_CONFIG_PATH --clear_cache -v"
5) Configure relaying parties
SSO Relaying parties can be configured per UI in the admin module or via cli using curl
- UI
Go to Admin > Applications > SSO and open the tab RELYING PARTIES in the SSO configuration dialog.
- CLI
#! /bin/sh
URL=http://web:4000/
USER=tine20admin
read PASS
TMP=$(curl -s $URL \
-H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'X-Tine20-ClientAssetHash: 64558b09a551cee0fac4c9f1dcd5c7a3ac5c0e34' \
-H 'X-Tine20-Request-Type: JSON' \
--insecure \
-d '{"jsonrpc":"2.0","method":"Tinebase.login","params":{"username":"'"$USER"'","password":"'"$PASS"'"},"id":0}' \
| jq -r '.result.sessionId, .result.jsonKey')
i=0
for line in $TMP; do
if [ $i -eq 0 ]; then
SESSIONID="$line"
else
JSONKEY="$line"
fi
echo "$line"
i=$((i+1))
done
export SESSIONID
export JSONKEY
- SAML2
The metadata URL of the tine ® idp (needed in config of the rp) is https://YOURTINEURL/sso/saml2/idpmetadata .
If not given tine ® fetches the AssertionConsumerService* and singleLogoutService* properties from the optional metaUrl.
#! /bin/sh
curl $URL \
-H 'Content-Type: application/json' \
-H 'Cookie: TINE20SESSID='"$SESSIONID"'' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'X-Tine20-JsonKey: '"$JSONKEY"'' \
-H 'X-Tine20-Request-Type: JSON' \
--insecure \
-d '{"jsonrpc":"2.0","method":"SSO.saveRelyingParty","params":{"recordData":{
"name": "saml-test-sp",
"label": "saml-test-sp",
"description": "you might test with ghcr.io/beryju/saml-test-sp",
"logo": null,
"config_class": "SSO_Model_Saml2RPConfig",
"config": {
"name": "saml-test-sp",
"entityid": "saml-test-sp",
"metaUrl": "http://localhost:4902/saml/metadata",
"AssertionConsumerServiceBinding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
"AssertionConsumerServiceLocation": "http://localhost:4902/saml/acs",
"singleLogoutServiceLocation": "http://localhost:4902/saml/slo",
"singleLogoutServiceBinding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
"attributeMapping": {
"uid": "accountEmailAddress"
},
"customHooks": {
"postAuthenticate": "/etc/tine20/samlPostAuthHook.php"
},
"id": "0"
}
},"duplicateCheck":true},"id":3}'
Custom attributes mappings
The mapping tine ® user attributes => SAML2 attributes can be customized the rp's attributeMapping property (JSON formatted)
{
"uid": "accountEmailAddress"
}
Custom attributes in SAML response
You can add custom attributes which are sent to the rp see customHooks property (JSON formatted) in the rp config.
<?php
// example SAML2 post authenticate hook
// adds the users groups to the attributes
$groupCtrl = Tinebase_Group::getInstance();
$userGroups = $groupCtrl->getMultiple($groupCtrl->getGroupMemberships($user->getId()));
$state['Attributes']['groups'] = array_values($userGroups->name); // value must be an array
EXAMPLE: add tine as iDP in sentry (tested with version 26.6.0 + tine 2026.11)
(see https://docs.sentry.io/organization/authentication/sso/#saml2-identity-providers)
- add relying party in tine via admin
- name: same as METADATA_URL (example: https://sentry.bla.blubb/saml/metadata/sentry/)
-
type: SAML2 Relying Party Config
- name: same as METADATA_URL
- entity id: same as METADATA_URL
- metadata_url: METADATA_URL (example: https://sentry.bla.blubb/saml/metadata/sentry/)
- Assertion Consumer Service Binding: :urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
- Assertion Consumer Service Location: example: https://sentry.bla.blubb/saml/acs/sentry/
- Logout Service Binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
- attribute mapping:
{ "uid": "accountId", "email": "accountEmailAddress", "first_name": "accountFirstName", "last_name": "accountLastName" }
-
connect to tine via sentry
- add tine metadata url -> https://my.tine.url/sso/saml2/idpmetadata
- add mapping (uid, email, first_name, last_name) -> see above
- done...
- OIDC
The OIDC provider url of the tine ® idp (needed in the config of the rp) is https://YOURTINEURL (no trailing slash).
#! /bin/sh
curl $URL \
-H 'Accept: */*' \
-H 'Content-Type: application/json' \
-H 'Cookie: TINE20SESSID='"$SESSIONID"'' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'X-Tine20-JsonKey: '"$JSONKEY"'' \
-H 'X-Tine20-Request-Type: JSON' \
--insecure \
-d '{"jsonrpc":"2.0","method":"SSO.saveRelyingParty","params":{"recordData":{
"name": "oidc-test-sp",
"label": "oidc test service provider",
"description": "you might test with ghcr.io/beryju/oidc-test-client",
"logo": "https://openid.net/wordpress-content/uploads/2014/09/openid-r-logo-900x360.png",
"config_class": "SSO_Model_OAuthOIdRPConfig",
"config": {
"redirect_urls": [
"http://localhost:4901/auth/callback"
],
"secret": "test-secret",
"is_confidential": false,
"id": "0"
}
},"duplicateCheck":true},"id":3}'
tine ® as Single Sign On Relaying Party (SSO RP)
tine ® can act as SSO Relaying Party for OIDC
1) Install SSO application
- Go to
setup.phpand make sure SSO is installed - In the UI go to
Admin>Applications>SSOand make sure SSO is activated
2) Configure identity providers (IdP)
SSO identity providers can be configured per UI in the admin module or via cli using curl
- UI
Go to Admin > Applications > SSO and open the tab EXTERNAL IDENTITY PROVIDERS in the SSO configuration dialog.
- CLI
#! /bin/sh
URL=http://web:4000/
USER=tine20admin
read PASS
TMP=$(curl -s $URL \
-H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'X-Tine20-ClientAssetHash: 64558b09a551cee0fac4c9f1dcd5c7a3ac5c0e34' \
-H 'X-Tine20-Request-Type: JSON' \
--insecure \
-d '{"jsonrpc":"2.0","method":"Tinebase.login","params":{"username":"'"$USER"'","password":"'"$PASS"'"},"id":0}' \
| jq -r '.result.sessionId, .result.jsonKey')
i=0
for line in $TMP; do
if [ $i -eq 0 ]; then
SESSIONID="$line"
else
JSONKEY="$line"
fi
echo "$line"
i=$((i+1))
done
export SESSIONID
export JSONKEY
NOTE: For following setup to work correctly, a
oidc-server-mockto127.0.0.1entry is required in/etc/hosts
#! /bin/sh
curl $URL \
-H 'Accept: */*' \
-H 'Content-Type: application/json' \
-H 'Cookie: TINE20SESSID='"$SESSIONID"'' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'X-Tine20-JsonKey: '"$JSONKEY"'' \
-H 'X-Tine20-Request-Type: JSON' \
--insecure \
-d '{"jsonrpc":"2.0","method":"SSO.saveExternalIdp","params":{"recordData":{
"name": "oidc-test-idp",
"config_class": "SSO_Model_ExIdp_OIdConfig",
"config": {
"name": "test idp",
"provider_url": "http://oidc-server-mock",
"issuer": "http://oidc-server-mock",
"client_id": "tine20",
"client_secret": "tine20"
},
"domains": [
{
"domain": "mail.test"
}
]
},"duplicateCheck":true},"id":3}'