api-challenger.rst (20584B)
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 RESTful 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 88 --------------- 89 Version History 90 --------------- 91 92 The current protocol version is **v6**. 93 94 * The Challenger SPA is currently targeting **v6**. 95 96 **Version history:** 97 98 * ``v6``: add the ``address_type`` field to ``/config`` 99 100 **Upcoming versions:** 101 102 * None anticipated. 103 104 **Ideas for future version:** 105 106 * ``vXXX``: marker for features not yet targeted for release 107 108 109 .. include:: tos.rst 110 111 .. _challenger-config: 112 113 ----------------------- 114 Receiving Configuration 115 ----------------------- 116 117 .. http:get:: /config 118 119 Obtain the key configuration settings of the storage service. 120 121 **Response:** 122 123 Returns a `ChallengerTermsOfServiceResponse`. 124 125 .. ts:def:: ChallengerTermsOfServiceResponse 126 127 interface ChallengerTermsOfServiceResponse { 128 // Name of the service 129 name: "challenger"; 130 131 // libtool-style representation of the Challenger protocol version, see 132 // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning 133 // The format is "current:revision:age". 134 version: string; 135 136 // URN of the implementation (needed to interpret 'revision' in version). 137 // @since v0, may become mandatory in the future. 138 implementation?: string; 139 140 // @since **v2**. 141 // Object; map of keys (names of the fields of the address 142 // to be entered by the user) to objects with a "regex" (string) 143 // containing an extended Posix regular expression for allowed 144 // address field values, and a "hint"/"hint_i18n" giving a 145 // human-readable explanation to display if the value entered 146 // by the user does not match the regex. Keys that are not mapped 147 // to such an object have no restriction on the value provided by 148 // the user. See "ADDRESS_RESTRICTIONS" in the challenger configuration. 149 restrictions: Object; 150 151 // @since **v6** 152 // Defines the set of fields asked to the user. 153 // The field names are registered via GANA at 154 // https://git.taler.net/gana.git/tree/gnu-taler-form-attributes 155 // email: CONTACT_EMAIL 156 // phone: CONTACT_PHONE 157 // postal: CONTACT_NAME, ADDRESS_LINES, ADDRESS_COUNTRY 158 // postal-ch: CONTACT_NAME, ADDRESS_LINES 159 address_type: "email" | "phone" | "postal" | "postal-ch"; 160 161 // Hint to show in the address bar for the user as an example for 162 // the format of the address. 163 address_hint: string; 164 } 165 166 .. _challenger-setup: 167 168 ----- 169 Setup 170 ----- 171 172 .. http:post:: /setup/$CLIENT_ID 173 174 This endpoint is used by the client to authorize the execution of an address 175 validation on its behalf. An ``Authorization`` header (for now always using 176 a ``Bearer`` token) should be included to provide the client's credentials 177 to authorize access to the challenger service. This token must match the 178 ``client_secret`` from the registration of the client with the challenger 179 service (which will also be used in the later ``/token`` request). 180 181 **Request:** 182 183 The body can be an address in JSON encoding to pre-initialize the address to 184 be used by challenger for this process. If the body is absent, the user will 185 have to enter the full address details. The specific address format depends 186 on the address type. However, `ChallengeSetupRequest` defines the shared 187 ``read_only`` bit that has a special meaning independent of the address type: 188 it informs Challenger that the address should not be editable. 189 190 Passing an address in the ``/setup`` body is supported @since protocol **v4**. 191 192 **Response:** 193 194 :http:statuscode:`200 OK`: 195 Response is a `ChallengeSetupResponse`. 196 :http:statuscode:`404 Not found`: 197 The backup service is unaware of a matching client. 198 or the credentials of the client are invalid. 199 200 **Details::** 201 202 .. ts:def:: ChallengeSetupRequest 203 204 interface ChallengeSetupRequest { 205 // If true, the given address should not be edited. 206 // Defaults to 'false' if not specified. 207 read_only?: boolean; 208 209 // Optional, additional fields to pre-populate 210 // the address to be validated. 211 // The fields depend on the challenger type. 212 [x: string]: any; 213 } 214 215 216 .. ts:def:: ChallengeSetupResponse 217 218 interface ChallengeSetupResponse { 219 // Nonce to use when constructing ``/authorize`` endpoint. 220 nonce: string; 221 } 222 223 224 .. _challenger-login: 225 226 ----- 227 Login 228 ----- 229 230 .. http:get:: /authorize/$NONCE 231 .. http:post:: /authorize/$NONCE 232 233 This is the "authorization" endpoint of the OAuth 2.0 protocol. This 234 endpoint is used by the user-agent. It will return a form to enter the 235 address. 236 237 The NONCE is a unique value identifying the challenge, should be shown to 238 the user so that they can recognize it when they receive the TAN code. 239 240 241 **Request:** 242 243 :query response_type: Must be ``code`` 244 :query client_id: Identifier of the client. 245 :query redirect_uri: URI-encoded redirection URI to use upon authorization. 246 :query state: Arbitrary client state to associate with the request. 247 :query scope: Not supported, any value is accepted. 248 :query code_challenge: A string to enhance security using PKCE (available since **v3**). 249 :query code_challenge_method: The method used for the code_challenge. Options are S256 (SHA-256) or plain (available since **v3**). 250 251 **Response:** 252 253 :http:statuscode:`200 OK`: 254 The the response is 255 a `ChallengeStatus`. Since protocol **v1**. 256 :http:statuscode:`400 Bad Request`: 257 The request does not follow the spec. 258 The response will include error 259 code, hint and detail. Since protocol **v1**. 260 :http:statuscode:`404 Not found`: 261 The service is unaware of a matching challenge. 262 The response will include error 263 code, hint and detail. Since protocol **v1**. 264 :http:statuscode:`406 Not Acceptable`: 265 The client ask for "text/html" and the backend installation does 266 not include the required HTML templates. 267 :http:statuscode:`500 Internal Server Error`: 268 Server is not able to respond due to internal problems. 269 The response will include error 270 code, hint and detail. Since protocol **v1**. 271 272 .. ts:def:: ChallengeStatus 273 274 interface ChallengeStatus { 275 // @deprecated since **v2**, use /config 276 restrictions?: Object; 277 278 // indicates if the given address cannot be changed anymore, the 279 // form should be read-only if set to true. 280 fix_address: boolean; 281 282 // form values from the previous submission if available, details depend 283 // on the ``ADDRESS_TYPE``, should be used to pre-populate the form 284 last_address?: Object; 285 286 // is the challenge already solved? 287 solved: boolean; 288 289 // number of times the address can still be changed, may or may not be 290 // shown to the user 291 changes_left: Integer; 292 293 // when we would re-transmit the challenge the next 294 // time (at the earliest) if requested by the user 295 // only present if challenge already created 296 // @since **v2** 297 retransmission_time: Timestamp; 298 299 // how many times might the PIN still be retransmitted 300 // only present if challenge already created 301 // @since **v2** 302 pin_transmissions_left?: Integer; 303 304 // how many times might the user still try entering the PIN code 305 // only present if challenge already created 306 // @since **v2** 307 auth_attempts_left?: Integer; 308 } 309 310 311 .. _challenger-challenge: 312 313 --------- 314 Challenge 315 --------- 316 317 .. http:post:: /challenge/$NONCE 318 319 This endpoint is used by the user-agent to submit the address to which a 320 challenge should be sent by the challenger service. 321 322 **Request:** 323 324 Body should use the mime-type "application/x-www-form-urlencoded". 325 The posted form data must contain an object that follow the restrictions 326 defined in :ref:`config <challenger-config>`. 327 328 **Response:** 329 330 :http:statuscode:`200 OK`: 331 The response is `ChallengeResponse`. Since protocol **v2**. 332 :http:statuscode:`400 Bad Request`: 333 The request does not follow the spec. 334 The response will include error 335 code, hint and detail. Since protocol **v1**. 336 :http:statuscode:`404 Not Found`: 337 The service is unaware of a matching challenge. 338 The response will include error 339 code, hint and detail. Since protocol **v1**. 340 :http:statuscode:`406 Not Acceptable`: 341 The client ask for "text/html" and the backend installation does 342 not include the required HTML templates. 343 :http:statuscode:`429 Too Many Requests`: 344 There have been too many attempts to request challenge 345 transmissions for this $NONCE. The user-agent should 346 wait and (eventually) request a fresh nonce to be set 347 up by the client. 348 The response will include error 349 code, hint and detail. Since protocol **v2**. 350 :http:statuscode:`500 Internal Server Error`: 351 Server is not able to respond due to internal problems. 352 The response will include error 353 code, hint and detail. Since protocol **v1**. 354 355 .. ts:def:: ChallengeResponse 356 357 // Union discriminated by the "type" field. 358 type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse 359 360 .. ts:def:: ChallengeRedirect 361 362 // @since **v2** 363 interface ChallengeRedirect { 364 // Union discriminator field. 365 type: "completed"; 366 367 // challenge is completed, use should redirect here 368 redirect_url: string; 369 } 370 371 .. ts:def:: ChallengeCreateResponse 372 373 interface ChallengeCreateResponse { 374 // Union discriminator field. 375 type: "created" 376 377 // how many more attempts are allowed, might be shown to the user, 378 // highlighting might be appropriate for low values such as 1 or 2 (the 379 // form will never be used if the value is zero) 380 attempts_left: Integer; 381 382 // the address that is being validated, might be shown or not 383 address: Object; 384 385 // true if we just retransmitted the challenge, false if we sent a 386 // challenge recently and thus refused to transmit it again this time; 387 // might make a useful hint to the user 388 transmitted: boolean; 389 390 // @deprecated in **v2**, use retransmission_time 391 next_tx_time?: string; 392 393 // when we would re-transmit the challenge the next 394 // time (at the earliest) if requested by the user 395 // @since **v2** 396 retransmission_time: Timestamp; 397 } 398 399 400 .. _challenger-solve: 401 402 ----- 403 Solve 404 ----- 405 406 .. http:post:: /solve/$NONCE 407 408 Used by the user-agent to submit an answer to the challenge. If the answer 409 is correct, the user will be redirected to the client's redirect URI, 410 otherwise the user may be given another chance to complete the process. 411 412 **Request:** 413 414 Body should use the mime-type "application/x-www-form-urlencoded". 415 The posted form data must contain a "pin" field. 416 417 **Response:** 418 419 :http:statuscode:`200 OK`: 420 If the request ask for application/json the response is 421 a `ChallengeSolveResponse`. Since protocol **v2**. 422 :http:statuscode:`302 Found`: 423 Only possible if request didn't ask for application/json. Since protocol **v2**. 424 The user is redirected to the redirect URI of the client to pass the 425 grant to the client. The target will be the redirect URI specified 426 by the client (during registration and again upon ``/authorize``), 427 plus a ``code`` argument with the authorization code, and the 428 ``state`` argument from the ``/authorize`` endpoint. 429 :http:statuscode:`400 Bad Request`: 430 The request does not follow the spec. 431 The response will include error 432 code, hint and detail. Since protocol **v1**. 433 :http:statuscode:`403 Forbidden`: 434 The response is `InvalidPinResponse`. Since protocol **v1**. 435 :http:statuscode:`404 Not found`: 436 The service is unaware of a matching challenge. 437 The response will include error 438 code, hint and detail. Since protocol **v1**. 439 :http:statuscode:`429 Too Many Requests`: 440 There have been too many attempts to solve the challenge 441 for this address (and $NONCE). The user-agent should 442 either try a different address (or wait and (eventually) 443 request a fresh nonce to be set up by the client). 444 The response will include error 445 code, hint and detail. Since protocol **v2**. 446 :http:statuscode:`500 Internal Server Error`: 447 Server is not able to respond due to internal problems. 448 The response will include error 449 code, hint and detail. Since protocol **v1**. 450 451 .. ts:def:: ChallengeSolveResponse 452 453 // Union discriminated by the "type" field. 454 type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse; 455 456 .. ts:def:: InvalidPinResponse 457 458 interface InvalidPinResponse { 459 // Union discriminator field. 460 type: "pending"; 461 462 // numeric Taler error code, should be shown to indicate the error 463 // compactly for reporting to developers 464 code: Integer; 465 466 // human-readable Taler error code, should be shown for the user to 467 // understand the error 468 hint: string; 469 470 // how many times is the user still allowed to change the address; 471 // if 0, the user should not be shown a link to jump to the 472 // address entry form 473 addresses_left: Integer; 474 475 // how many times might the PIN still be retransmitted 476 pin_transmissions_left: Integer; 477 478 // how many times might the user still try entering the PIN code 479 auth_attempts_left: Integer; 480 481 // if true, the PIN was not even evaluated as the user previously 482 // exhausted the number of attempts 483 exhausted: boolean; 484 485 // if true, the PIN was not even evaluated as no challenge was ever 486 // issued (the user must have skipped the step of providing their 487 // address first!) 488 no_challenge: boolean; 489 } 490 491 .. _challenger-auth: 492 493 ---- 494 Auth 495 ---- 496 497 .. http:post:: /token 498 499 This is the token endpoint of the OAuth 2.0 specification. 500 This endpoint is used by the client to provide its authorization code, 501 demonstrating that it has the right to learn a particular user's validated 502 address. In return, the challenger service returns the access token. 503 Renewal is not supported. 504 505 **Request:** 506 507 The request must include an ``application/www-form-urlencoded`` body 508 specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code`` 509 and ``grant_type``. The ``grant_type`` must be set to 510 ``authorization_code``. The ``redirect_uri`` must match the URI from 511 ``/authorize``. The ``code`` must be the authorization code that ``/solve`` 512 returned to the user. The ``client_id`` and ``client_secret`` must match 513 the usual client credentials. Since protocol **v3**, ``code_verifier`` can also be included. 514 515 **Response:** 516 517 Error responses follow RFC 6749, section 5.2 with an "error" field in JSON, 518 as well as also returning GNU Taler style error messages. 519 520 :http:statuscode:`200 OK`: 521 The body will be a `ChallengerAuthResponse` 522 :http:statuscode:`401 Unauthorized`: 523 The ``code_verifier`` is not matching the saved ones. (Since **v3**) 524 :http:statuscode:`403 Forbidden`: 525 The credentials of the client are invalid. 526 :http:statuscode:`404 Not found`: 527 The service is unaware of a matching login process. 528 529 **Details::** 530 531 .. ts:def:: ChallengerAuthResponse 532 533 interface ChallengerAuthResponse { 534 // Token used to authenticate access in ``/info``. 535 access_token: string; 536 537 // Type of the access token. 538 token_type: "Bearer"; 539 540 // Amount of time that an access token is valid (in seconds). 541 expires_in: Integer; 542 543 } 544 545 546 .. _challenger-info: 547 548 ---- 549 Info 550 ---- 551 552 .. http:get:: /info 553 554 This userinfo endpoint of the OAuth 2.0 specification. 555 This endpoint is used by the client to obtain the user's validated address. 556 557 **Request:** 558 559 Must include the token returned to the client from the ``/token`` endpoint 560 as a ``Bearer`` token in an ``Authorization`` header. 561 562 **Response:** 563 564 :http:statuscode:`200 OK`: 565 The body contains the address as a `ChallengerInfoResponse`. 566 :http:statuscode:`403 Forbidden`: 567 The bearer token is missing or invalid (malformed). 568 :http:statuscode:`404 Not found`: 569 The bearer token is invalid (includes unknown or expired). 570 571 **Details::** 572 573 .. ts:def:: ChallengerInfoResponse 574 575 interface ChallengerInfoResponse { 576 577 // Unique ID of the record within Challenger 578 // (identifies the rowid of the token). 579 id: Integer; 580 581 // Address that was validated. 582 // Key-value pairs, details depend on the 583 // address_type. 584 address: Object; 585 586 // Type of the address. 587 address_type: string; 588 589 // How long do we consider the address to be 590 // valid for this user. 591 expires: Timestamp; 592 593 }