github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/docs/delegated-auth.md (about)

     1  [Table of contents](README.md#table-of-contents)
     2  
     3  # Delegated authentication
     4  
     5  In general, the cozy stack manages the authentication itself. In some cases, an
     6  integration with other softwares can be mandatory. It's possible to use JWT or
     7  OpenID Connect, with a bit of configuration to do that.
     8  
     9  ## JWT
    10  
    11  To enable an external system to create links with a JWT to log in users for
    12  cozy instances in a given context, we just need to add the secret to use for
    13  checking the JWT in the config, like this:
    14  
    15  ```yaml
    16  authentication:
    17    the-context-name:
    18      jwt_secret: s3cr3t
    19  ```
    20  
    21  The external system can then create a JWT, with the parameter `name` as the
    22  instance domain, and send the user to `https://<instance>/?jwt=...`. The user
    23  will be logged in, and redirected to its default application.
    24  
    25  ## Open ID Connect
    26  
    27  OpenID Connect can also be used, and is more adapted when the users don't
    28  always come from the authentication provider.
    29  
    30  For OpenID Connect, there are more configuration parameters and they must be
    31  configured per context. A context is set of configuration parameters and each
    32  Cozy instance belongs to one context.
    33  
    34  ```yaml
    35  authentication:
    36    the-context-name:
    37      disable_password_authentication: false
    38      oidc:
    39        client_id: aClientID
    40        client_secret: s3cret3
    41        scope: openid profile
    42        login_domain: login.mycozy.cloud
    43        redirect_uri: https://oauthcallback.mycozy.cloud/oidc/redirect
    44        authorize_url: https://identity-provider/path/to/authorize
    45        token_url: https://identity-provider/path/to/token
    46        userinfo_url: https://identity-provider/path/to/userinfo
    47        userinfo_instance_field: cozy_number
    48        userinfo_instance_prefix: name
    49        userinfo_instance_suffix: .mycozy.cloud
    50        allow_custom_instance: false
    51        allow_oauth_token: false
    52        id_token_jwk_url: https://identity-provider/path/to/jwk
    53  ```
    54  
    55  Let's see what it means:
    56  
    57  - `disable_password_authentication` can be set to `true` to disable the classic
    58    password authentication on the Cozy, and forces the user to login with OpenID
    59    Connect.
    60  
    61  And in the `oidc` section, we have:
    62  
    63  - `client_id` and `client_secret` are the OAuth client that will be used to
    64    talk to the identity provider
    65  - `scope` is the OAuth scope parameter (it is often `openid profile`)
    66  - `login_domain` is a domain that is not tied to an instance, but allows to
    67    login with OIDC with the provider configured on this context
    68  - `redirect_uri` is where the user will be redirected by the identity provider
    69    after login (it must often be declared when creating the OAuth client, and we
    70    have to use a static hostname, not the hostname of a cozy instance)
    71  - `logout_url` can be set to redirect the user to this URL after they have been
    72    logged out
    73  - `token_url`, `authorize_url`, and `userinfo_url` are the URLs used to talk to
    74    the identity provider (they ofter can be found by the discovery mechanism of
    75    OpenID Connect with the names `token_endpoint`, `authorization_endpoint`, and
    76    `userinfo_endpoint`)
    77  - `userinfo_instance_field` is the JSON field to use in the UserInfo response
    78    to know the cozy instance of the logged in user.
    79  - `userinfo_instance_prefix` and `userinfo_instance_suffix` are optional, and
    80    will be put before and after the field fetched from the UserInfo response to
    81    give the complete instance URL
    82  - `allow_custom_instance` can be set to true to let the user chooses their
    83    instance name
    84  - `allow_oauth_token` must be set to true to enable the
    85    `POST /oidc/access_token` route (see below for more details).
    86  
    87  With the example config, if the UserInfo response contains `"cozy_number":
    88  "00001"`, the user can login on the instance `name00001.mycozy.cloud`.
    89  
    90  When `allow_custom_instance` is set to true, the stack will look at the `sub`
    91  field in the UserInfo response, and checks that it matches the `oidc_id` set
    92  on this instance (and the `userinfo_instance_*` and `login_domain` fields are
    93  ignored). If `id_token_jwk_url` is set, the client can send the ID token from
    94  the provider instead of the access token. This token will be checked with the
    95  key fetched from this URL, and the `sub` field of it must match the `oidc_id`
    96  set in the instance.
    97  
    98  ### Routes
    99  
   100  Let's see the 3 routes used in this process
   101  
   102  #### GET /oidc/start
   103  
   104  To start the OpenID Connect dance, the user can go to this URL. It will
   105  redirect him/her to the identity provider with the rights parameter. The user
   106  will also be redirected here if they are not connected and that the password
   107  authentication is disabled.
   108  
   109  ```http
   110  GET /oidc/start HTTP/1.1
   111  Host: name00001.mycozy.cloud
   112  ```
   113  
   114  ```http
   115  HTTP/1.1 303 See Other
   116  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
   117  ```
   118  
   119  #### GET /oidc/redirect
   120  
   121  Then, the user can log in on the identity provider, and then he/she will be
   122  redirected to this URL. Note that the URL is on a generic domain: the stack
   123  will redirect the user to his/her instance (where it's possible to create
   124  cookies to log in the user).
   125  
   126  ```http
   127  GET /oidc/redirect?state=9f6873dfce7d&code=ccd0032a HTTP/1.1
   128  Host: oauthcallback.mycozy.cloud
   129  ```
   130  
   131  ```http
   132  HTTP/1.1 303 See Other
   133  Location: https://name00001.mycozy.cloud/oidc/login?state=9f6873dfce7d&code=ccd0032a
   134  ```
   135  
   136  #### GET /oidc/login
   137  
   138  On this route, the stack can create the session for the user, with the cookies.
   139  
   140  ```http
   141  GET /oidc/login?code=ccd0032a HTTP/1.1
   142  Host: name00001.mycozy.cloud
   143  ```
   144  
   145  ```http
   146  HTTP/1.1 303 See Other
   147  Set-Cookie: ...
   148  Location: https://name00001-home.mycozy.cloud/
   149  ```
   150  
   151  If the `allow_oauth_token` option is enabled, it's possible to use an
   152  access_token instead of code on this URL.
   153  
   154  If the `id_token_jwk_url` option is enabled, it's possible to use an
   155  id_token instead.
   156  
   157  #### POST /oidc/twofactor
   158  
   159  If the instance is protected with two-factor authentication, the login
   160  route will render an HTML page with JavaScript to check if the user has trusted
   161  the device. And the JavaScript submit a form to this route. If the trusted
   162  device token is set, a session will be created. Else, a mail with a code is
   163  sent, and the user is redirected to a page where they can type the two-factor
   164  code.
   165  
   166  ```http
   167  POST /oidc/twofactor HTTP/1.1
   168  Host: name00001.mycozy.cloud
   169  Content-Type: application/x-www-form-urlencoded
   170  
   171  trusted-device-token=xxx&access-token=yyy&redirect=&confirm=
   172  ```
   173  
   174  ```http
   175  HTTP/1.1 303 See Other
   176  Set-Cookie: ...
   177  Location: https://name00001-home.mycozy.cloud/
   178  ```
   179  
   180  #### POST /oidc/access_token
   181  
   182  This additional route can be used by an OAuth client (like a mobile app) when
   183  delegated authentication via OpenID Connect is enabled. It allows the client to
   184  obtain an `access_token` for requesting the cozy-stack in exchange of a token
   185  valid on the OpenID Connect Identity Provider.
   186  
   187  ```http
   188  POST /oidc/access_token HTTP/1.1
   189  Host: name00001.mycozy.cloud
   190  Accept: application/json
   191  Content-Type: application/json
   192  ```
   193  
   194  ```json
   195  {
   196    "client_id": "55eda056e85468fdfe2c8440d4009cbe",
   197    "client_secret": "DttCGIUOTniVNkivR_CsZ_xRoME9gghN",
   198    "scope": "io.cozy.files io.cozy.photos.albums",
   199    "oidc_token": "769fa760-59de-11e9-a167-9bab3784e3e7"
   200  }
   201  ```
   202  
   203  ```http
   204  HTTP/1.1 200 OK
   205  Content-Type: application/json
   206  ```
   207  
   208  ```json
   209  {
   210    "access_token": "ooch1Yei",
   211    "token_type": "bearer",
   212    "refresh_token": "ui0Ohch8",
   213    "scope": "io.cozy.files io.cozy.photos.albums"
   214  }
   215  ```
   216  
   217  If `id_token_jwk_url` option is set, the client can send an `id_token` instead
   218  of an `oidc_token` in the payload.
   219  
   220  If the flagship makes the request, it also can use a delegated code obtained
   221  from the cloudery, by using `code` instead of `oidc_token`.
   222  
   223  **Note:** if the OAuth client asks for a `*` scope and has not been certified
   224  as the flagship app, this request will return:
   225  
   226  ```http
   227  HTTP/1.1 202 Accepted
   228  Content-Type: application/json
   229  ```
   230  
   231  ```json
   232  {
   233    "session_code": "ZmY4ODI3NGMtOTY1Yy0xMWVjLThkMDgtMmI5M2"
   234  }
   235  ```
   236  
   237  The `session_code` can be put in the query string while opening the OAuth
   238  authorize page. It will be used to open the session, and let the user type the
   239  6-digits code they have received by mail to confirm that they want to use this
   240  app as the flagship app.
   241  
   242  ##### Special case of 2FA
   243  
   244  When 2FA is enabled on the instance, the stack will first respond with:
   245  
   246  ```http
   247  HTTP/1.1 403 Forbidden
   248  Content-Type: application/json
   249  ```
   250  
   251  ```json
   252  {
   253    "error": "two factor needed",
   254    "two_factor_token": "123123123123"
   255  }
   256  ```
   257  
   258  and the client must ask the user to type its 6-digits code, and then make again
   259  the request:
   260  
   261  ```json
   262  {
   263    "access_token": "ooch1Yei",
   264    "token_type": "bearer",
   265    "refresh_token": "ui0Ohch8",
   266    "scope": "io.cozy.files io.cozy.photos.albums",
   267    "two_factor_token": "123123123123",
   268    "two_factor_passcode": "678678"
   269  }
   270  ```
   271  
   272  ## FranceConnect
   273  
   274  It is pretty much the same thing as OIDC. It's logical as FranceConnect is an
   275  OIDC provider. But we have made a special case for the login page. The
   276  differences are that the flow is started with `GET /oidc/franceconnect`
   277  (instead of `GET /oidc/start`) and the configuration looks like this:
   278  
   279  ```yaml
   280  authentication:
   281    the-context-name:
   282      franceconnect:
   283        client_id: aClientID
   284        client_secret: s3cret3
   285        scope: openid email
   286        redirect_uri: https://oauthcallback.mycozy.cloud/oidc/redirect
   287        authorize_url: https://identity-provider/path/to/authorize
   288        token_url: https://identity-provider/path/to/token
   289        userinfo_url: https://identity-provider/path/to/userinfo
   290  ```
   291  
   292  The last 3 URL can be omited for production.