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.