Skip to content

Table of contents

Delegated authentication

In general, the cozy stack manages the authentication itself. In some cases, an integration with other softwares can be mandatory. It’s possible to use JWT or OpenID Connect, with a bit of configuration to do that.

JWT

To enable an external system to create links with a JWT to log in users for cozy instances in a given context, we just need to add the secret to use for checking the JWT in the config, like this:

authentication:
  the-context-name:
    jwt_secret: s3cr3t

The external system can then create a JWT, with the parameter name as the instance domain, and send the user to https://<instance>/?jwt=.... The user will be logged in, and redirected to its default application.

Open ID Connect

OpenID Connect can also be used, and is more adapted when the users don’t always come from the authentication provider.

For OpenID Connect, there are more configuration parameters and they must be configured per context. A context is set of configuration parameters and each Cozy instance belongs to one context.

authentication:
  the-context-name:
    disable_password_authentication: false
    oidc:
      client_id: aClientID
      client_secret: s3cret3
      scope: openid profile
      login_domain: login.mycozy.cloud
      redirect_uri: https://oauthcallback.mycozy.cloud/oidc/redirect
      authorize_url: https://identity-provider/path/to/authorize
      token_url: https://identity-provider/path/to/token
      userinfo_url: https://identity-provider/path/to/userinfo
      userinfo_instance_field: cozy_number
      userinfo_instance_prefix: name
      userinfo_instance_suffix: .mycozy.cloud
      allow_custom_instance: false
      allow_oauth_token: false
      id_token_jwk_url: https://identity-provider/path/to/jwk

Let’s see what it means:

  • disable_password_authentication can be set to true to disable the classic password authentication on the Cozy, and forces the user to login with OpenID Connect.

And in the oidc section, we have:

  • client_id and client_secret are the OAuth client that will be used to talk to the identity provider
  • scope is the OAuth scope parameter (it is often openid profile)
  • login_domain is a domain that is not tied to an instance, but allows to login with OIDC with the provider configured on this context
  • redirect_uri is where the user will be redirected by the identity provider after login (it must often be declared when creating the OAuth client, and we have to use a static hostname, not the hostname of a cozy instance)
  • logout_url can be set to redirect the user to this URL after they have been logged out
  • token_url, authorize_url, and userinfo_url are the URLs used to talk to the identity provider (they ofter can be found by the discovery mechanism of OpenID Connect with the names token_endpoint, authorization_endpoint, and userinfo_endpoint)
  • userinfo_instance_field is the JSON field to use in the UserInfo response to know the cozy instance of the logged in user.
  • userinfo_instance_prefix and userinfo_instance_suffix are optional, and will be put before and after the field fetched from the UserInfo response to give the complete instance URL
  • allow_custom_instance can be set to true to let the user chooses their instance name
  • allow_oauth_token must be set to true to enable the POST /oidc/access_token route (see below for more details).

With the example config, if the UserInfo response contains "cozy_number": "00001", the user can login on the instance name00001.mycozy.cloud.

When allow_custom_instance is set to true, the stack will look at the sub field in the UserInfo response, and checks that it matches the oidc_id set on this instance (and the userinfo_instance_* and login_domain fields are ignored). If id_token_jwk_url is set, the client can send the ID token from the provider instead of the access token. This token will be checked with the key fetched from this URL, and the sub field of it must match the oidc_id set in the instance.

Routes

Let’s see the 3 routes used in this process

GET /oidc/start

To start the OpenID Connect dance, the user can go to this URL. It will redirect him/her to the identity provider with the rights parameter. The user will also be redirected here if they are not connected and that the password authentication is disabled.

GET /oidc/start HTTP/1.1
Host: name00001.mycozy.cloud
HTTP/1.1 303 See Other
Location: https://identity-provider/path/to/authorize?response_type=code&state=9f6873dfce7d&scope=openid+profile&client_id=aClientID&nonce=94246498&redirect_uri=https://oauthcallback.mycozy.cloud/oidc/redirect

GET /oidc/redirect

Then, the user can log in on the identity provider, and then he/she will be redirected to this URL. Note that the URL is on a generic domain: the stack will redirect the user to his/her instance (where it’s possible to create cookies to log in the user).

GET /oidc/redirect?state=9f6873dfce7d&code=ccd0032a HTTP/1.1
Host: oauthcallback.mycozy.cloud
HTTP/1.1 303 See Other
Location: https://name00001.mycozy.cloud/oidc/login?state=9f6873dfce7d&code=ccd0032a

GET /oidc/login

On this route, the stack can create the session for the user, with the cookies.

GET /oidc/login?code=ccd0032a HTTP/1.1
Host: name00001.mycozy.cloud
HTTP/1.1 303 See Other
Set-Cookie: ...
Location: https://name00001-home.mycozy.cloud/

If the allow_oauth_token option is enabled, it’s possible to use an access_token instead of code on this URL.

If the id_token_jwk_url option is enabled, it’s possible to use an id_token instead.

POST /oidc/twofactor

If the instance is protected with two-factor authentication, the login route will render an HTML page with JavaScript to check if the user has trusted the device. And the JavaScript submit a form to this route. If the trusted device token is set, a session will be created. Else, a mail with a code is sent, and the user is redirected to a page where they can type the two-factor code.

POST /oidc/twofactor HTTP/1.1
Host: name00001.mycozy.cloud
Content-Type: application/x-www-form-urlencoded

trusted-device-token=xxx&access-token=yyy&redirect=&confirm=
HTTP/1.1 303 See Other
Set-Cookie: ...
Location: https://name00001-home.mycozy.cloud/

POST /oidc/access_token

This additional route can be used by an OAuth client (like a mobile app) when delegated authentication via OpenID Connect is enabled. It allows the client to obtain an access_token for requesting the cozy-stack in exchange of a token valid on the OpenID Connect Identity Provider.

POST /oidc/access_token HTTP/1.1
Host: name00001.mycozy.cloud
Accept: application/json
Content-Type: application/json
{
  "client_id": "55eda056e85468fdfe2c8440d4009cbe",
  "client_secret": "DttCGIUOTniVNkivR_CsZ_xRoME9gghN",
  "scope": "io.cozy.files io.cozy.photos.albums",
  "oidc_token": "769fa760-59de-11e9-a167-9bab3784e3e7"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
  "access_token": "ooch1Yei",
  "token_type": "bearer",
  "refresh_token": "ui0Ohch8",
  "scope": "io.cozy.files io.cozy.photos.albums"
}

If id_token_jwk_url option is set, the client can send an id_token instead of an oidc_token in the payload.

If the flagship makes the request, it also can use a delegated code obtained from the cloudery, by using code instead of oidc_token.

Note: if the OAuth client asks for a * scope and has not been certified as the flagship app, this request will return:

HTTP/1.1 202 Accepted
Content-Type: application/json
{
  "session_code": "ZmY4ODI3NGMtOTY1Yy0xMWVjLThkMDgtMmI5M2"
}

The session_code can be put in the query string while opening the OAuth authorize page. It will be used to open the session, and let the user type the 6-digits code they have received by mail to confirm that they want to use this app as the flagship app.

Special case of 2FA

When 2FA is enabled on the instance, the stack will first respond with:

HTTP/1.1 403 Forbidden
Content-Type: application/json
{
  "error": "two factor needed",
  "two_factor_token": "123123123123"
}

and the client must ask the user to type its 6-digits code, and then make again the request:

{
  "access_token": "ooch1Yei",
  "token_type": "bearer",
  "refresh_token": "ui0Ohch8",
  "scope": "io.cozy.files io.cozy.photos.albums",
  "two_factor_token": "123123123123",
  "two_factor_passcode": "678678"
}

FranceConnect

It is pretty much the same thing as OIDC. It’s logical as FranceConnect is an OIDC provider. But we have made a special case for the login page. The differences are that the flow is started with GET /oidc/franceconnect (instead of GET /oidc/start) and the configuration looks like this:

authentication:
  the-context-name:
    franceconnect:
      client_id: aClientID
      client_secret: s3cret3
      scope: openid email
      redirect_uri: https://oauthcallback.mycozy.cloud/oidc/redirect
      authorize_url: https://identity-provider/path/to/authorize
      token_url: https://identity-provider/path/to/token
      userinfo_url: https://identity-provider/path/to/userinfo

The last 3 URL can be omited for production.