api-challenger.rst (20277B)
1 .. 2 This file is part of GNU TALER. 3 Copyright (C) 2023, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 16 @author Christian Grothoff 17 18 .. _challenger-api: 19 20 ====================== 21 Challenger Service API 22 ====================== 23 24 The challenger service validates that a user is able to receive challenges at 25 an address (such as e-mail or SMS) and allows an OAuth 2.0 client to obtain 26 access to these validated addresses. 27 28 The high-level flow is that an OAuth 2.0 client is first registered with the 29 challenger service (via command-line). Using the command-line tool will print 30 the resulting client ID to the console. 31 32 .. note:: 33 34 Right now, registration of a unique redirection URI is *mandatory* for 35 each client. If multiple redirection URIs are needed, it is suggested to 36 just register additional clients. (While OAuth 2.0 would support not 37 registering fixed redirection URIs with a client, this is not recommended 38 as it would create an open redirector.) 39 40 Once a client is registered, that client can use the challenger service when 41 it needs a user to prove that the user is able to receive messages at a 42 particular address. However, asking a user to prove access to a particular 43 address can be expensive as it may involve sending an SMS or even postal mail 44 depending on the type of address. Thus, challenger does not allow a user 45 agent to begin an address validation process without prior approval by a 46 registered client. Thus, the process begins with a ``/setup/$CLIENT_ID`` request where a 47 client requests challenger to begin an address validation request. The 48 ``/setup/$CLIENT_ID`` response contains a ``nonce`` which is then used to construct the 49 URL of the endpoint to which the client must redirect the user-agent to begin 50 the address validation and authorization process. 51 52 The client then redirects the user-agent to the ``/authorize/$NONCE`` endpoint 53 of the challenger service, adding its ``state``, ``client_id`` and 54 ``redirect_uri`` as query parameters. The ``redirect_uri`` must match the 55 redirect URI registered with the client. From this endpoint, the challenger 56 service will return a Web page asking the user to provide its address. 57 58 .. note:: 59 60 Challenger is a bit unusual in that the ``$NONCE`` in the endpoint URL 61 makes the authorization endpoint URL (deliberately) unpredictable, while 62 for many other OAuth 2.0 APIs this endpoint is static. However, this is 63 compliant with OAuth 2.0 as determining the authorization URL is left out 64 of the scope of the standard. 65 66 When the user has filled in the form with their address, it will be submitted 67 to the ``/challenge/$NONCE`` endpoint and the challenger service will send a 68 challenge to the user's address and generate an HTML form asking the user to 69 enter the received challenge value. 70 71 The user can then enter the answer to the challenge which is then submitted to 72 the ``/solve/$NONCE`` endpoint. If the answer is correct, the user agent will 73 be redirected to the client redirect URI that was specified by the OAuth 2.0 74 client upon ``/authorize``, together with an authorization grant encoded in 75 the redirection URI. 76 77 Given this authorization grant, the OAuth 2.0 client can then use the 78 ``/token`` endpoint to obtain an access token which will grant it access to 79 the resource. 80 81 Using the ``/info`` endpoint the client can then finally obtain the (now) 82 verified address of the user. 83 84 .. contents:: Table of Contents 85 :local: 86 87 .. include:: tos.rst 88 89 .. _challenger-config: 90 91 ----------------------- 92 Receiving Configuration 93 ----------------------- 94 95 .. http:get:: /config 96 97 Obtain the key configuration settings of the storage service. 98 This specification corresponds to ``current`` protocol being version **v6**. 99 100 **Response:** 101 102 Returns a `ChallengerTermsOfServiceResponse`. 103 104 .. ts:def:: ChallengerTermsOfServiceResponse 105 106 interface ChallengerTermsOfServiceResponse { 107 // Name of the service 108 name: "challenger"; 109 110 // libtool-style representation of the Challenger protocol version, see 111 // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning 112 // The format is "current:revision:age". 113 version: string; 114 115 // URN of the implementation (needed to interpret 'revision' in version). 116 // @since v0, may become mandatory in the future. 117 implementation?: string; 118 119 // @since **v2**. 120 // Object; map of keys (names of the fields of the address 121 // to be entered by the user) to objects with a "regex" (string) 122 // containing an extended Posix regular expression for allowed 123 // address field values, and a "hint"/"hint_i18n" giving a 124 // human-readable explanation to display if the value entered 125 // by the user does not match the regex. Keys that are not mapped 126 // to such an object have no restriction on the value provided by 127 // the user. See "ADDRESS_RESTRICTIONS" in the challenger configuration. 128 restrictions: Object; 129 130 // @since **v6** 131 // Defines the set of fields asked to the user. 132 // The field names are registered via GANA at 133 // https://git.taler.net/gana.git/tree/gnu-taler-form-attributes 134 // email: CONTACT_EMAIL 135 // phone: CONTACT_PHONE 136 // postal: CONTACT_NAME, ADDRESS_LINES, ADDRESS_COUNTRY 137 // postal-ch: CONTACT_NAME, ADDRESS_LINES 138 address_type: "email" | "phone" | "postal" | "postal-ch"; 139 140 // Hint to show in the address bar for the user as an example for 141 // the format of the address. 142 address_hint: string; 143 } 144 145 .. _challenger-setup: 146 147 ----- 148 Setup 149 ----- 150 151 .. http:post:: /setup/$CLIENT_ID 152 153 This endpoint is used by the client to authorize the execution of an address 154 validation on its behalf. An ``Authorization`` header (for now always using 155 a ``Bearer`` token) should be included to provide the client's credentials 156 to authorize access to the challenger service. This token must match the 157 ``client_secret`` from the registration of the client with the challenger 158 service (which will also be used in the later ``/token`` request). 159 160 **Request:** 161 162 The body can be an address in JSON encoding to pre-initialize the address to 163 be used by challenger for this process. If the body is absent, the user will 164 have to enter the full address details. The specific address format depends 165 on the address type. However, `ChallengeSetupRequest` defines the shared 166 ``read_only`` bit that has a special meaning independent of the address type: 167 it informs Challenger that the address should not be editable. 168 169 Passing an address in the ``/setup`` body is supported @since protocol **v4**. 170 171 **Response:** 172 173 :http:statuscode:`200 OK`: 174 Response is a `ChallengeSetupResponse`. 175 :http:statuscode:`404 Not found`: 176 The backup service is unaware of a matching client. 177 or the credentials of the client are invalid. 178 179 **Details::** 180 181 .. ts:def:: ChallengeSetupRequest 182 183 interface ChallengeSetupRequest { 184 // If true, the given address should not be edited. 185 // Defaults to 'false' if not specified. 186 read_only?: boolean; 187 188 // Optional, additional fields to pre-populate 189 // the address to be validated. 190 // The fields depend on the challenger type. 191 [x: string]: any; 192 } 193 194 195 .. ts:def:: ChallengeSetupResponse 196 197 interface ChallengeSetupResponse { 198 // Nonce to use when constructing ``/authorize`` endpoint. 199 nonce: string; 200 } 201 202 203 .. _challenger-login: 204 205 ----- 206 Login 207 ----- 208 209 .. http:get:: /authorize/$NONCE 210 .. http:post:: /authorize/$NONCE 211 212 This is the "authorization" endpoint of the OAuth 2.0 protocol. This 213 endpoint is used by the user-agent. It will return a form to enter the 214 address. 215 216 The NONCE is a unique value identifying the challenge, should be shown to 217 the user so that they can recognize it when they receive the TAN code. 218 219 220 **Request:** 221 222 :query response_type: Must be ``code`` 223 :query client_id: Identifier of the client. 224 :query redirect_uri: URI-encoded redirection URI to use upon authorization. 225 :query state: Arbitrary client state to associate with the request. 226 :query scope: Not supported, any value is accepted. 227 :query code_challenge: A string to enhance security using PKCE (available since **v3**). 228 :query code_challenge_method: The method used for the code_challenge. Options are S256 (SHA-256) or plain (available since **v3**). 229 230 **Response:** 231 232 :http:statuscode:`200 OK`: 233 The the response is 234 a `ChallengeStatus`. Since protocol **v1**. 235 :http:statuscode:`400 Bad Request`: 236 The request does not follow the spec. 237 The response will include error 238 code, hint and detail. Since protocol **v1**. 239 :http:statuscode:`404 Not found`: 240 The service is unaware of a matching challenge. 241 The response will include error 242 code, hint and detail. Since protocol **v1**. 243 :http:statuscode:`406 Not Acceptable`: 244 The client ask for "text/html" and the backend installation does 245 not include the required HTML templates. 246 :http:statuscode:`500 Internal Server Error`: 247 Server is not able to respond due to internal problems. 248 The response will include error 249 code, hint and detail. Since protocol **v1**. 250 251 .. ts:def:: ChallengeStatus 252 253 interface ChallengeStatus { 254 // @deprecated since **v2**, use /config 255 restrictions?: Object; 256 257 // indicates if the given address cannot be changed anymore, the 258 // form should be read-only if set to true. 259 fix_address: boolean; 260 261 // form values from the previous submission if available, details depend 262 // on the ``ADDRESS_TYPE``, should be used to pre-populate the form 263 last_address?: Object; 264 265 // is the challenge already solved? 266 solved: boolean; 267 268 // number of times the address can still be changed, may or may not be 269 // shown to the user 270 changes_left: Integer; 271 272 // when we would re-transmit the challenge the next 273 // time (at the earliest) if requested by the user 274 // only present if challenge already created 275 // @since **v2** 276 retransmission_time: Timestamp; 277 278 // how many times might the PIN still be retransmitted 279 // only present if challenge already created 280 // @since **v2** 281 pin_transmissions_left?: Integer; 282 283 // how many times might the user still try entering the PIN code 284 // only present if challenge already created 285 // @since **v2** 286 auth_attempts_left?: Integer; 287 } 288 289 290 .. _challenger-challenge: 291 292 --------- 293 Challenge 294 --------- 295 296 .. http:post:: /challenge/$NONCE 297 298 This endpoint is used by the user-agent to submit the address to which a 299 challenge should be sent by the challenger service. 300 301 **Request:** 302 303 Body should use the mime-type "application/x-www-form-urlencoded". 304 The posted form data must contain an object that follow the restrictions 305 defined in :ref:`config <challenger-config>`. 306 307 **Response:** 308 309 :http:statuscode:`200 OK`: 310 The response is `ChallengeResponse`. Since protocol **v2**. 311 :http:statuscode:`400 Bad Request`: 312 The request does not follow the spec. 313 The response will include error 314 code, hint and detail. Since protocol **v1**. 315 :http:statuscode:`404 Not Found`: 316 The service is unaware of a matching challenge. 317 The response will include error 318 code, hint and detail. Since protocol **v1**. 319 :http:statuscode:`406 Not Acceptable`: 320 The client ask for "text/html" and the backend installation does 321 not include the required HTML templates. 322 :http:statuscode:`429 Too Many Requests`: 323 There have been too many attempts to request challenge 324 transmissions for this $NONCE. The user-agent should 325 wait and (eventually) request a fresh nonce to be set 326 up by the client. 327 The response will include error 328 code, hint and detail. Since protocol **v2**. 329 :http:statuscode:`500 Internal Server Error`: 330 Server is not able to respond due to internal problems. 331 The response will include error 332 code, hint and detail. Since protocol **v1**. 333 334 .. ts:def:: ChallengeResponse 335 336 // Union discriminated by the "type" field. 337 type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse 338 339 .. ts:def:: ChallengeRedirect 340 341 // @since **v2** 342 interface ChallengeRedirect { 343 // Union discriminator field. 344 type: "completed"; 345 346 // challenge is completed, use should redirect here 347 redirect_url: string; 348 } 349 350 .. ts:def:: ChallengeCreateResponse 351 352 interface ChallengeCreateResponse { 353 // Union discriminator field. 354 type: "created" 355 356 // how many more attempts are allowed, might be shown to the user, 357 // highlighting might be appropriate for low values such as 1 or 2 (the 358 // form will never be used if the value is zero) 359 attempts_left: Integer; 360 361 // the address that is being validated, might be shown or not 362 address: Object; 363 364 // true if we just retransmitted the challenge, false if we sent a 365 // challenge recently and thus refused to transmit it again this time; 366 // might make a useful hint to the user 367 transmitted: boolean; 368 369 // @deprecated in **v2**, use retransmission_time 370 next_tx_time?: string; 371 372 // when we would re-transmit the challenge the next 373 // time (at the earliest) if requested by the user 374 // @since **v2** 375 retransmission_time: Timestamp; 376 } 377 378 379 .. _challenger-solve: 380 381 ----- 382 Solve 383 ----- 384 385 .. http:post:: /solve/$NONCE 386 387 Used by the user-agent to submit an answer to the challenge. If the answer 388 is correct, the user will be redirected to the client's redirect URI, 389 otherwise the user may be given another chance to complete the process. 390 391 **Request:** 392 393 Body should use the mime-type "application/x-www-form-urlencoded". 394 The posted form data must contain a "pin" field. 395 396 **Response:** 397 398 :http:statuscode:`200 OK`: 399 If the request ask for application/json the response is 400 a `ChallengeSolveResponse`. Since protocol **v2**. 401 :http:statuscode:`302 Found`: 402 Only possible if request didn't ask for application/json. Since protocol **v2**. 403 The user is redirected to the redirect URI of the client to pass the 404 grant to the client. The target will be the redirect URI specified 405 by the client (during registration and again upon ``/authorize``), 406 plus a ``code`` argument with the authorization code, and the 407 ``state`` argument from the ``/authorize`` endpoint. 408 :http:statuscode:`400 Bad Request`: 409 The request does not follow the spec. 410 The response will include error 411 code, hint and detail. Since protocol **v1**. 412 :http:statuscode:`403 Forbidden`: 413 The response is `InvalidPinResponse`. Since protocol **v1**. 414 :http:statuscode:`404 Not found`: 415 The service is unaware of a matching challenge. 416 The response will include error 417 code, hint and detail. Since protocol **v1**. 418 :http:statuscode:`429 Too Many Requests`: 419 There have been too many attempts to solve the challenge 420 for this address (and $NONCE). The user-agent should 421 either try a different address (or wait and (eventually) 422 request a fresh nonce to be set up by the client). 423 The response will include error 424 code, hint and detail. Since protocol **v2**. 425 :http:statuscode:`500 Internal Server Error`: 426 Server is not able to respond due to internal problems. 427 The response will include error 428 code, hint and detail. Since protocol **v1**. 429 430 .. ts:def:: ChallengeSolveResponse 431 432 // Union discriminated by the "type" field. 433 type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse; 434 435 .. ts:def:: InvalidPinResponse 436 437 interface InvalidPinResponse { 438 // Union discriminator field. 439 type: "pending"; 440 441 // numeric Taler error code, should be shown to indicate the error 442 // compactly for reporting to developers 443 code: Integer; 444 445 // human-readable Taler error code, should be shown for the user to 446 // understand the error 447 hint: string; 448 449 // how many times is the user still allowed to change the address; 450 // if 0, the user should not be shown a link to jump to the 451 // address entry form 452 addresses_left: Integer; 453 454 // how many times might the PIN still be retransmitted 455 pin_transmissions_left: Integer; 456 457 // how many times might the user still try entering the PIN code 458 auth_attempts_left: Integer; 459 460 // if true, the PIN was not even evaluated as the user previously 461 // exhausted the number of attempts 462 exhausted: boolean; 463 464 // if true, the PIN was not even evaluated as no challenge was ever 465 // issued (the user must have skipped the step of providing their 466 // address first!) 467 no_challenge: boolean; 468 } 469 470 .. _challenger-auth: 471 472 ---- 473 Auth 474 ---- 475 476 .. http:post:: /token 477 478 This is the token endpoint of the OAuth 2.0 specification. 479 This endpoint is used by the client to provide its authorization code, 480 demonstrating that it has the right to learn a particular user's validated 481 address. In return, the challenger service returns the access token. 482 Renewal is not supported. 483 484 **Request:** 485 486 The request must include an ``application/www-form-urlencoded`` body 487 specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code`` 488 and ``grant_type``. The ``grant_type`` must be set to 489 ``authorization_code``. The ``redirect_uri`` must match the URI from 490 ``/authorize``. The ``code`` must be the authorization code that ``/solve`` 491 returned to the user. The ``client_id`` and ``client_secret`` must match 492 the usual client credentials. Since protocol **v3**, ``code_verifier`` can also be included. 493 494 **Response:** 495 496 Error responses follow RFC 6749, section 5.2 with an "error" field in JSON, 497 as well as also returning GNU Taler style error messages. 498 499 :http:statuscode:`200 OK`: 500 The body will be a `ChallengerAuthResponse` 501 :http:statuscode:`401 Unauthorized`: 502 The ``code_verifier`` is not matching the saved ones. (Since **v3**) 503 :http:statuscode:`403 Forbidden`: 504 The credentials of the client are invalid. 505 :http:statuscode:`404 Not found`: 506 The service is unaware of a matching login process. 507 508 **Details::** 509 510 .. ts:def:: ChallengerAuthResponse 511 512 interface ChallengerAuthResponse { 513 // Token used to authenticate access in ``/info``. 514 access_token: string; 515 516 // Type of the access token. 517 token_type: "Bearer"; 518 519 // Amount of time that an access token is valid (in seconds). 520 expires_in: Integer; 521 522 } 523 524 525 .. _challenger-info: 526 527 ---- 528 Info 529 ---- 530 531 .. http:get:: /info 532 533 This userinfo endpoint of the OAuth 2.0 specification. 534 This endpoint is used by the client to obtain the user's validated address. 535 536 **Request:** 537 538 Must include the token returned to the client from the ``/token`` endpoint 539 as a ``Bearer`` token in an ``Authorization`` header. 540 541 **Response:** 542 543 :http:statuscode:`200 OK`: 544 The body contains the address as a `ChallengerInfoResponse`. 545 :http:statuscode:`403 Forbidden`: 546 The bearer token is missing or invalid (malformed). 547 :http:statuscode:`404 Not found`: 548 The bearer token is invalid (includes unknown or expired). 549 550 **Details::** 551 552 .. ts:def:: ChallengerInfoResponse 553 554 interface ChallengerInfoResponse { 555 556 // Unique ID of the record within Challenger 557 // (identifies the rowid of the token). 558 id: Integer; 559 560 // Address that was validated. 561 // Key-value pairs, details depend on the 562 // address_type. 563 address: Object; 564 565 // Type of the address. 566 address_type: string; 567 568 // How long do we consider the address to be 569 // valid for this user. 570 expires: Timestamp; 571 572 }