Authentication 

Security decides which caller may perform an operation. This chapter explains how a client becomes that caller: as a frontend user, a backend user, or a script holding a key.

tca_api does not log anyone in itself. It reads the identity that TYPO3 or an installed extension established for the request.

How tca_api sees identity 

Before any access check, tca_api resolves the caller of the request, the principal, through a chain of authenticators:

Authenticator Identity
API keys A key sent as X-API-Key or Authorization: Bearer. Only registered when the api_key extension is installed, see Scripts and machine-to-machine.
Session The frontend user and the backend user that TYPO3 restored from their session cookies (fe_typo_user, be_typo_user). Always active.
Custom Any AuthenticatorInterface of another extension, see Authenticators.

Credentials sent with the request are checked before the session. A request without any credentials is anonymous and can only reach PUBLIC operations.

The two error codes mean different things:

Status Meaning
401 Unauthorized Credentials were sent, but they are invalid: a wrong, expired or revoked key, or a key used from a forbidden IP address or site. The WWW-Authenticate header names the scheme. The request is rejected even if a valid session exists.
403 Forbidden The caller is anonymous, or authenticated but not allowed: the role does not match, the record belongs to another user, or the key's scopes do not cover the operation.

Browser and single-page apps 

In the browser the regular TYPO3 login is the authentication. A user who logged in through felogin holds a frontend session cookie, and every API request that carries the cookie acts as that user. A backend editor who is logged in to the TYPO3 backend in the same browser also carries the backend session cookie and satisfies BE_USER, and BE_ADMIN for administrators.

On the same origin nothing has to be configured: fetch() sends the cookies of the own origin by default.

Cross-origin clients 

A single-page app on another origin needs three things:

  1. Allow credentials and name the app's origin in the site settings:

    config/sites/<site>/settings.yaml
    tca_api:
      corsEnabled: true
      corsOrigin: 'https://app.example.com'
      corsAllowCredentials: true
    Copied!
  2. Send the cookies with every request:

    const response = await fetch('https://www.example.com/_api/articles', {
        credentials: 'include',
    });
    Copied!
  3. Make sure the browser sends the cookie at all. The frontend session cookie defaults to SameSite=Lax, which browsers only send to the same site (app.example.com and www.example.com are the same site). For an app on a different domain, the cookie must be SameSite=None, which requires HTTPS:

    config/system/additional.php
    $GLOBALS['TYPO3_CONF_VARS']['FE']['cookieSameSite'] = 'none';
    Copied!

    This affects every frontend session of the installation, so prefer hosting the app on a subdomain of the site.

Logging in from a script 

Logging in a frontend user programmatically, e.g. with curl, is impractical. Since TYPO3 v13 every frontend login has to carry a signed request token (__RequestToken) that is bound to a nonce cookie. A script would first have to load a page with the felogin form, extract the token, keep the cookie jar and then post the credentials. For scripts and server-to-server calls use API keys instead.

Scripts and machine-to-machine 

The api_key extension (documentation) adds stateless API keys. tca_api suggests it but does not require it; once it is installed, tca_api picks it up automatically.

composer require maikschneider/api-key
Copied!

Create a key on the command line. The key is printed once and cannot be recovered:

# Service key: no user, satisfies AccessRole::API_KEY
vendor/bin/typo3 apikey:create --title="CI deploy" --scope=articles:write --expires="+90 days"

# Acts as frontend user 12, with its groups
vendor/bin/typo3 apikey:create --title="Partner import" --fe-user=12

# Acts as backend user 3
vendor/bin/typo3 apikey:create --title="Editorial sync" --be-user=3
Copied!

Send it in either header:

curl -H "X-API-Key: ${TYPO3_API_KEY}" https://example.com/_api/articles
curl -H "Authorization: Bearer ${TYPO3_API_KEY}" https://example.com/_api/articles
Copied!

What a key may do depends on what it is bound to:

Key Satisfies
Service key API_KEY
Bound to a frontend user FE_USER, FE_GROUP, OWNER and API_KEY
Bound to a backend user BE_USER, BE_ADMIN (for admins) and API_KEY

The key's scopes always restrict it, also for a key bound to a user, see Scopes. A key without scopes is unrestricted, so give every key the narrowest scopes it needs.

Keyed requests are stateless: they create no fe_sessions row, send no Set-Cookie and leave lastlogin untouched.

How the bridge maps keys to principals, adds X-API-Key to the CORS headers and documents the security schemes is described in API keys.

Other extensions 

Extensions that log a user in through TYPO3's authentication services work as well, because tca_api reads the resulting session:

Extension Notes
kuhschnappel/authtoken Tokens for frontend users, sent in an X-User-Token header. Supports TYPO3 up to v13.4. It relies on FE_alwaysFetchUser, so every cookie-less request creates an fe_sessions row and sends Set-Cookie.
r3h6/oauth2-server An OAuth2 server for frontend users. Supports TYPO3 up to v13.4.

Session-based extensions that only log in backend users, such as OAuth2 or OpenID Connect login providers for the backend, apply to browsers, not to scripts: the frontend only restores a backend user when the backend session cookie is present.

Swagger UI 

Swagger UI at {apiPrefix}swagger-ui offers the authentication schemes of the OpenAPI document in its Authorize dialog:

  • Sessions (FrontendSession, BackendSession): log in on the website or in the backend in the same browser and reload Swagger UI. The browser sends the session cookie by itself.
  • API keys (ApiKey, ApiKeyBearer): click Authorize and paste a key. The key is kept across reloads until you click Logout.