Skip to content

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://my.tine.url/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://my.tine.url (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}'