1.8. Challenger Service API

The challenger service validates that a user is able to receive challenges at an address (such as e-mail or SMS) and allows an OAuth 2.0 client to obtain access to these validated addresses.

The high-level flow is that an OAuth 2.0 client is first registered with the challenger service (via command-line). Using the command-line tool will print the resulting client ID to the console.

Note

The current service mandates that redirection URIs start with “http://” or “https://”. See issue #7838 for what should be done to lift this restriction.

Note

Right now, registration of a unique redirection URI is mandatory for each client. If multiple redirection URIs are needed, it is suggested to just register additional clients. (While OAuth 2.0 would support not registering fixed redirection URIs with a client, this is not recommended as it would create an open redirector.)

Once a client is registered, that client can use the challenger service when it needs a user to prove that the user is able to receive messages at a particular address. However, asking a user to prove access to a particular address can be expensive as it may involve sending an SMS or even postal mail depending on the type of address. Thus, challenger does not allow a user agent to begin an address validation process without prior approval by a registered client. Thus, the process begins with a /setup/$CLIENT_ID request where a client requests challenger to begin an address validation request. The /setup/$CLIENT_ID response contains a nonce which is then used to construct the URL of the endpoint to which the client must redirect the user-agent to begin the address validation and authorization process.

The client then redirects the user-agent to the /authorize/$NONCE endpoint of the challenger service, adding its state, client_id and redirect_uri as query parameters. The redirect_uri must match the redirect URI registered with the client. From this endpoint, the challenger service will return a Web page asking the user to provide its address.

Note

Challenger is a bit unusual in that the $NONCE in the endpoint URL makes the authorization endpoint URL (deliberately) unpredictable, while for many other OAuth 2.0 APIs this endpoint is static. However, this is compliant with OAuth 2.0 as determining the authorization URL is left out of the scope of the standard.

When the user has filled in the form with their address, it will be submitted to the /challenge/$NONCE endpoint and the challenger service will send a challenge to the user’s address and generate an HTML form asking the user to enter the received challenge value.

The user can then enter the answer to the challenge which is then submitted to the /solve/$NONCE endpoint. If the answer is correct, the user agent will be redirected to the client redirect URI that was specified by the OAuth 2.0 client upon /authorize, together with an authorization grant encoded in the redirection URI.

Given this authorization grant, the OAuth 2.0 client can then use the /token endpoint to obtain an access token which will grant it access to the resource.

Using the /info endpoint the client can then finally obtain the (now) verified address of the user.

1.8.1. Terms of service API

These APIs allow clients to obtain the terms of service and the privacy policy of a service.

GET /terms

Get the terms of service of the service. The endpoint will consider the “Accept” and “Accept-Language” and “Accept-Encoding” headers when generating a response. Specifically, it will try to find a response with an acceptable mime-type, then pick the version in the most preferred language of the user, and finally apply compression if that is allowed by the client and deemed beneficial.

The endpoint will set an “Etag”, and subsequent requests of the same client should provide the tag in an “If-None-Match” header to detect if the terms of service have changed. If not, a “304 Not Modified” response will be returned. Note that the “304 Not Modified” will also be returned if the client changed the “Accept-Language” or “Accept-Encoding” header. Thus, if the client would like to download the resource in a different language or format, the “If-None-Match” header must be omitted.

If the “Etag” is missing, the client should not cache the response and instead prompt the user again at the next opportunity. This is usually only the case if the terms of service were not configured correctly.

The “Etag” is generated from the first 256 bits of the SHA-512 hash over the terms and encoded in Crockford base-32. However, this behavior is not normative and clients MUST NOT rely on it.

A “Taler-Terms-Version” header is generated to indicate the legal version of the terms. This header will change whenever something legally changed in the terms of service and the user must review and accept the terms of service again. If the “Taler-Terms-Version” is identical to one that the user has already accepted, there is no need for the user to review the terms again.

When returning a full response (not a “304 Not Modified”), the server should also include a “Avail-Languages” header which includes a comma-separated list of the languages in which the terms of service are available in (see availability hints specification). Clients can use this to generate a language switcher for users that may not have expressed a proper language preference.

Response:

200 OK:

The body is the terms of service in the requested encoding and language.

501 Not Implemented:

The exchange lacks a valid terms of service configuration. A human-readable error message is returned. Wallets should not require the human to accept any terms of service (and do not need to show this message).

GET /privacy

Get the privacy policy of the service. Behaves the same way as The “/terms” endpoint, except that it returns the privacy policy instead of the terms of service.

Response:

200 OK:

The body is the privacy policy in the requested encoding and language.

501 Not Implemented:

The exchange lacks a valid terms of service configuration. A human-readable error message is returned. Wallets should not require the human to accept any terms of service (and do not need to show this message).

1.8.2. Receiving Configuration

GET /config

Obtain the key configuration settings of the storage service. This specification corresponds to current protocol being version 4.

Response:

Returns a ChallengerTermsOfServiceResponse.

interface ChallengerTermsOfServiceResponse {
  // Name of the service
  name: "challenger";

  // libtool-style representation of the Challenger protocol version, see
  // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
  // The format is "current:revision:age".
  version: string;

  // URN of the implementation (needed to interpret 'revision' in version).
  // @since v0, may become mandatory in the future.
  implementation?: string;

  // @since **v2**.
  // Object; map of keys (names of the fields of the address
  // to be entered by the user) to objects with a "regex" (string)
  // containing an extended Posix regular expression for allowed
  // address field values, and a "hint"/"hint_i18n" giving a
  // human-readable explanation to display if the value entered
  // by the user does not match the regex. Keys that are not mapped
  // to such an object have no restriction on the value provided by
  // the user.  See "ADDRESS_RESTRICTIONS" in the challenger configuration.
  restrictions: Object;

  // @since **v2**.
  address_type: "email" | "phone";
}

1.8.3. Setup

POST /setup/$CLIENT_ID

This endpoint is used by the client to authorize the execution of an address validation on its behalf. An Authorization header (for now always using a Bearer token) should be included to provide the client’s credentials to authorize access to the challenger service. This token must match the client_secret from the registration of the client with the challenger service (which will also be used in the later /token request).

Request:

The body can be an address in JSON encoding to pre-initialize the address to be used by challenger for this process. If the body is absent, the user will have to enter the full address details. The specific address format depends on the address type.

Passing an address in the /setup body is supported @since protocol v4.

Response:

200 OK:

Response is a ChallengeSetupResponse.

404 Not found:

The backup service is unaware of a matching client. or the credentials of the client are invalid.

Details::

interface ChallengeSetupResponse {
  // Nonce to use when constructing /authorize endpoint.
  nonce: string;
}

1.8.4. Login

GET /authorize/$NONCE
POST /authorize/$NONCE

This is the “authorization” endpoint of the OAuth 2.0 protocol. This endpoint is used by the user-agent. It will return a form to enter the address.

The NONCE is a unique value identifying the challenge, should be shown to the user so that they can recognize it when they receive the TAN code.

Request:

Query Parameters:
  • response_type – Must be code

  • client_id – Identifier of the client.

  • redirect_uri – URI-encoded redirection URI to use upon authorization.

  • state – Arbitrary client state to associate with the request.

  • scope – Not supported, any value is accepted.

  • code_challenge – A string to enhance security using PKCE (available since v3).

  • code_challenge_method – The method used for the code_challenge. Options are S256 (SHA-256) or plain (available since v3).

Response:

200 OK:

The the response is a ChallengeStatus. Since protocol v1.

400 Bad Request:

The request does not follow the spec. The response will include error code, hint and detail. Since protocol v1.

404 Not found:

The service is unaware of a matching challenge. The response will include error code, hint and detail. Since protocol v1.

406 Not Acceptable:

The client ask for “text/html” and the backend installation does not include the required HTML templates.

500 Internal Server Error:

Server is not able to respond due to internal problems. The response will include error code, hint and detail. Since protocol v1.

interface ChallengeStatus {
  // @deprecated since **v2**, use /config
  restrictions?: Object;

  // indicates if the given address cannot be changed anymore, the
  // form should be read-only if set to true.
  fix_address: boolean;

  // form values from the previous submission if available, details depend
  // on the ADDRESS_TYPE, should be used to pre-populate the form
  last_address?: Object;

  // is the challenge already solved?
  solved: boolean;

  // number of times the address can still be changed, may or may not be
  // shown to the user
  changes_left: Integer;

  // when we would re-transmit the challenge the next
  // time (at the earliest) if requested by the user
  // only present if challenge already created
  // @since **v2**
  retransmission_time: Timestamp;

  // how many times might the PIN still be retransmitted

  // how many times might the PIN still be retransmitted
  // only present if challenge already created
  // @since **v2**
  pin_transmissions_left?: Integer;

  // how many times might the user still try entering the PIN code
  // only present if challenge already created
  // @since **v2**
  auth_attempts_left?: Integer;
}

1.8.5. Challenge

POST /challenge/$NONCE

This endpoint is used by the user-agent to submit the address to which a challenge should be sent by the challenger service.

Request:

Body should use the mime-type “application/x-www-form-urlencoded”. The posted form data must contain an object that follow the restrictions defined in config.

Response:

200 OK:

The response is ChallengeResponse. Since protocol v2.

302 Found:

Only possible if request didn’t ask for application/json. Since protocol v2. The user is redirected to the redirect URI of the client to pass the grant to the client. The target will be the redirect URI specified by the client (during registration and again upon /authorize), plus a code argument with the authorization code, and the state argument from the /authorize endpoint.

400 Bad Request:

The request does not follow the spec. The response will include error code, hint and detail. Since protocol v1.

404 Not Found:

The service is unaware of a matching challenge. The response will include error code, hint and detail. Since protocol v1.

406 Not Acceptable:

The client ask for “text/html” and the backend installation does not include the required HTML templates.

429 Too Many Requests:

There have been too many attempts to request challenge transmissions for this $NONCE. The user-agent should wait and (eventually) request a fresh nonce to be set up by the client. The response will include error code, hint and detail. Since protocol v2.

500 Internal Server Error:

Server is not able to respond due to internal problems. The response will include error code, hint and detail. Since protocol v1.

// Union discriminated by the "type" field.
type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
// @since **v2**
interface ChallengeRedirect {
  // Union discriminator field.
  type: "completed";

  // challenge is completed, use should redirect here
  redirect_url: string;
}
interface ChallengeCreateResponse {
   // Union discriminator field.
   type: "created"

   // how many more attempts are allowed, might be shown to the user,
   // highlighting might be appropriate for low values such as 1 or 2 (the
   // form will never be used if the value is zero)
   attempts_left: Integer;

   // the address that is being validated, might be shown or not
   address: Object;

   // true if we just retransmitted the challenge, false if we sent a
   // challenge recently and thus refused to transmit it again this time;
   // might make a useful hint to the user
   transmitted: boolean;

   // @deprecated in **v2**, use retransmission_time
   next_tx_time?: string;

   // when we would re-transmit the challenge the next
   // time (at the earliest) if requested by the user
   // @since **v2**
   retransmission_time: Timestamp;
 }

1.8.6. Solve

POST /solve/$NONCE

Used by the user-agent to submit an answer to the challenge. If the answer is correct, the user will be redirected to the client’s redirect URI, otherwise the user may be given another chance to complete the process.

Request:

Body should use the mime-type “application/x-www-form-urlencoded”. The posted form data must contain a “pin” field.

Response:

200 OK:

If the request ask for application/json the response is a ChallengeSolveResponse. Since protocol v2.

302 Found:

Only possible if request didn’t ask for application/json. Since protocol v2. The user is redirected to the redirect URI of the client to pass the grant to the client. The target will be the redirect URI specified by the client (during registration and again upon /authorize), plus a code argument with the authorization code, and the state argument from the /authorize endpoint.

400 Bad Request:

The request does not follow the spec. The response will include error code, hint and detail. Since protocol v1.

403 Forbidden:

The response is InvalidPinResponse. Since protocol v1.

404 Not found:

The service is unaware of a matching challenge. The response will include error code, hint and detail. Since protocol v1.

429 Too Many Requests:

There have been too many attempts to solve the challenge for this address (and $NONCE). The user-agent should either try a different address (or wait and (eventually) request a fresh nonce to be set up by the client). The response will include error code, hint and detail. Since protocol v2.

500 Internal Server Error:

Server is not able to respond due to internal problems. The response will include error code, hint and detail. Since protocol v1.

// Union discriminated by the "type" field.
type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse;
interface InvalidPinResponse {
  // Union discriminator field.
  type: "pending";

  // numeric Taler error code, should be shown to indicate the error
  // compactly for reporting to developers
  ec: Integer;

  // human-readable Taler error code, should be shown for the user to
  // understand the error
  hint: string;

  // how many times is the user still allowed to change the address;
  // if 0, the user should not be shown a link to jump to the
  // address entry form
  addresses_left: Integer;

  // how many times might the PIN still be retransmitted
  pin_transmissions_left: Integer;

  // how many times might the user still try entering the PIN code
  auth_attempts_left: Integer;

  // if true, the PIN was not even evaluated as the user previously
  // exhausted the number of attempts
  exhausted: boolean;

  // if true, the PIN was not even evaluated as no challenge was ever
  // issued (the user must have skipped the step of providing their
  // address first!)
  no_challenge: boolean;
}

1.8.7. Auth

POST /token

This is the token endpoint of the OAuth 2.0 specification. This endpoint is used by the client to provide its authorization code, demonstrating that it has the right to learn a particular user’s validated address. In return, the challenger service returns the access token. Renewal is not supported.

Request:

The request must include an application/www-form-urlencoded body specifying the client_id, redirect_uri, client_secret, code and grant_type. The grant_type must be set to authorization_code. The redirect_uri must match the URI from /authorize. The code must be the authorization code that /solve returned to the user. The client_id and client_secret must match the usual client credentials. Since protocol v3, code_verifier can also be included.

Response:

Error responses follow RFC 6749, section 5.2 with an “error” field in JSON, as well as also returning GNU Taler style error messages.

200 OK:

The body will be a ChallengerAuthResponse

401 Unauthorized:

The code_verifier is not matching the saved ones. (Since v3)

403 Forbidden:

The credentials of the client are invalid.

404 Not found:

The service is unaware of a matching login process.

Details::

interface ChallengerAuthResponse {
  // Token used to authenticate access in /info.
  access_token: string;

  // Type of the access token.
  token_type: "Bearer";

  // Amount of time that an access token is valid (in seconds).
  expires_in: Integer;

}

1.8.8. Info

GET /info

This userinfo endpoint of the OAuth 2.0 specification. This endpoint is used by the client to obtain the user’s validated address.

Request:

Must include the token returned to the client from the /token endpoint as a Bearer token in an Authorization header.

Response:

200 OK:

The body contains the address as a ChallengerInfoResponse.

403 Forbidden:

The bearer token is missing or invalid (malformed).

404 Not found:

The bearer token is invalid (includes unknown or expired).

Details::

interface ChallengerInfoResponse {

  // Unique ID of the record within Challenger
  // (identifies the rowid of the token).
  id: Integer;

  // Address that was validated.
  // Key-value pairs, details depend on the
  // address_type.
  address: Object;

 // Type of the address.
  address_type: string;

  // How long do we consider the address to be
  // valid for this user.
  expires: Timestamp;

}