Zum Inhalt

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.php and make sure SSO is installed
  • In the UI go to Admin > Applications > SSO and 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

./conf.d/sso.inc.php
<?php

return [
    'SSO' => [
        'pwdLessLogin' => 'both',
        'oauth2' => [
            'enabled' => true,
        ],
        'saml2' => [
            'enabled' => true,
            'tineLogout' => true,
        ],
    ],
];

4) Clear config cache

clearCache
#! /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

Login
#! /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.

Add SAML2 RP
#! /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)

Example to map user email to SAML2 uid
{
    "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.

Example post auth hook
<?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).

Add OIDC RP
#! /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.php and make sure SSO is installed
  • In the UI go to Admin > Applications > SSO and 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

Login
#! /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-mock to 127.0.0.1 entry is required in /etc/hosts

Add foreign mock IDP
#! /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}'