i-TegoDocumentationAuthentification > OpenID Connect OAuth Server dedicated > Develop

This topic is for developers who want to use OAuth Server byDnC for their applications.
Find more information in the french version.

Q&A for authors and developpers

  publié le par DnC

The Author is a privileged user of OAuth Server. In particular, he owns one or more client applications that he has registered on the server using this site. He is a developer or has the knowledge.

Is OAuthSD compatible with OPenID Connect ?

OAuthSD aims to comply with the IETF specifications as published by the OPenID Foundation.
OAuthSD is 100% compliant for the Authentication Code flow, see : OAuthSD towards the OPenID Connect certification.
Note that :
- OAuthSD does not implement some flows, see : OpenID Connect : Summary of all authorization flows.
- OAuthSD evolves quickly and new compliance tests will be done,
- OAuthSD imposes some restrictions that go in the direction of security ; thus, the state parameter is mandatory in requests to Authorize, while the specification does not require it.
- OAuthSD offers extensive features that, however, do not contradict the specifications. The SLI mechanism is a good example : the OIDC specifications define everything needed for silent re-authentication (especially with the prompt = ’none’ parameter), but this would still have to be done by going back and forth between the server and the client application. OAuthSD processes this at the Authorise controller level using the SLI cookie, avoiding the developers a tedious adaptation work of the applications.
- OAuthSD adds some additions to the specification, without impacting compliance. Thus, the Authorize controller can be queried in Ajax and return the remaining session time. See : Monitoring de l’état de l’authentification et SLO, Avertir de la fin de session OIDC et la prolonger.

What does OAuthSD bring to security ?

The OIDC specifications do not take into account certain threats. For example the "Broken End-User Authentication" attack is not taken into account by the specifications, the proof is the fact that the parameter state is not obligatory.
The focus is on the eviction of a client application or a questionable user-agent. For example, there are now 12 tests in the Authorize controller to eliminate dubious requests.

In the field of security, OAuthSD is not only an authentication server but a supervisor.

For example, in the "Monitor" pages, the history of incidents is dated to better than the microsecond to facilitate cross-referencing. This requires that other histories are also microsecond timed [1].

OAuthSD also includes an administrator alert by E-mail or SMS, as well as an interface for tracking and alerting with PRTG.

The events history is in a syslog-compatible format for processing by intrusion detection, tracking, and alerting applications.

I can read criticisms about some flows, why implement them ?

It must be considered that the OIDC server is as secure as the least secure flow that it implements.
An amazing feature of OAuth 2.0 is to offer very insecure flows compared to the Authorization Code flow. Yet this one is really only complete in its OpenID Connect (not OAuth 2.0) version which implements the identity token.
Opening the possibility that the server responds to various requests (such as implicit and hybrid flows) without controlling the client application can be considered a security breach [2] !
We want to provide a 100% OpenID Connect compatible server. At the same time, developers and administrators need to be aware that exposing the server to implicit and hybrid flows is a security breach. For this reason, configuration constant is used to choose using implicit flows or not. One could thus answer only requests of types Authorization Code and Client Credentials.

Can we do it all with the Authorization Code flow ?

We think so, see for example : Applications natives : un exemple avec Windev.
The Client Credentials feed is also useful for an application’s access to protected resources. But it is possible to use the Authorization Code flow for this, see : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.
This remains an open point for discussion.

What does "There is more information, log in to see them" ?

When this message appears at the bottom of an article, it means that there is information reserved for privileged users of this site. If you have write permission on the topic, you will be allowed to see this text after logging in.

Notes

[1The project is that the core of the OIDC server will be encapsulated in a Docker component in which there will also be the database and an application firewall. The latter will be coupled to OIDC events to react immediately.

[2It may be the reason why you can read here : "However, as the focus was on the interoperability, less focus was given to the security issues around the implementation".

Link a client application to the OAuthSD server

  publié le par DnC

This article is for a developer.

It describes how to adapt an application to make it an OpenID Connect client and how to register it on OAuth Server byDnC.

This assumes that the developer or his organization is registered on the server as an Application Administrator.

The process has two aspects :

- on the OAuthSD server side, register the application,
- on the side of the client application, insert the necessary code to ensure the link with the OAuth server.

1. Register as an Application Administrator

Registration is here : Registration form.

Carefully fill out your Application Administrator form, as most of this information is communicated to the end users you need to gain trust.

Warning : do not confuse the registration as an Application Administrator (a developer who registers his client application so that he can delegate authentication to OAuthSD) with the registration of a user as an end user of client applications, which is done here : I’m signing on.

Notes :
- The term "Application Administrator" distinguishes the developer or owner of a client application that registers on this server, the end user, who must also register, but otherwise. In the underlying data model, it is also the editorial object of SPIP (the authors table), while the end user is a separate table.

2. Register the client application on the OAuthSD server

In the Manage section, go to Add (Register) a client application and fill out the form :

- Client Id (required) : String identifying the application in a unique way. Enter a short name with no spaces or special characters other than ’_’. This identifier must be unique for all administrators of applications registered on the server. It is visible to the public and should therefore be representative of the application and your business.

- Secret Client (required) : a short string that can be likened to a strong password. This code must remain secret. It has to be provided by the client application when requesting the token.

- Redirect uri (required) : URI back to the client application. This is the address at which the OAuth server returns to the client with the result of the authentication. OpenID Connect allows multiple URIs to be mentionned.

- Grant Type (required) : Except special case, select at least "Authorization Code". For Hybrid feeds, select "Authorization Code" and "Implicit". See OpenID Connect et OAuth 2.0 : Synthèse des flux d’autorisation (Grant Type).

- Scopes (required) : List of Scopes allowed for the application, separated by a space. The ’openid’ scope is mandatory. The standard scopes of OpenID Connect are : profile, email, address, phone. See the section : Réponse UserInfo. The ’offline_access’ scope is used to obtain a refresh token. In addition to these scopes, particular scopes can be defined for a given application. Example : "openid profile email administrator".

- User id If a user name (username) is registered here, only this user will be able to login, this identifier being fixed in the authentication form. In the general case this field will be empty.

Check your entries and press the "Save" button.

Finally, navigate to https://oa.dnc.global/keys to create the entry for the new public / private key pair.

You will be able to find the application and modify it in the section All your client applications.

Notes :
- OAuthSD creates a public / private key pair for the client application. If you want to change it, go to All your client applications and select the "keys" action corresponding to the application.

3. Insert the necessary code in the application

Although OpenID Connect is built on OAuth 2.0, do not expect to reuse the code written for this protocol. Indeed, OpenID Connect is a higher level abstraction layer, involving different exchanges and endpoints, which requires the code to be rewritten. Also note that OpenID is not the same as OpenID Connect.

If the client application is designed to delegate its authentications to a server with the OpenID Connect standard, there is nothing more to do.

If the application on which the client application is built offers an OpenID Connect plugin, all you need to do is install it. We offer some plugins here : Adapting applications.

Otherwise, it’s a developer business. There are two adaptations to make :

- Write the code located at the Return URI to the client application (Endpoint Redirection). This involves requesting the OAuthSD server an access token and an identification token for the application, from the Authorization code returned by the server.

- Write the authentication procedure adapted to the technique of the client application. Generally, it will be an additional authentication module, or a re-writing of the authentication code. See : Plugin OpenID Connect Client pour SPIP, Extension OpenID Connect pour phpBB, "OpenID Connect Generic Client" extension for WordPress.

Optionally, we can also :
- View the status of the OIDC session and manage it (Monitoring).

A simple case

Consuming a protected HTTP REST API is extremely simple. Here is a basic example, just to test the operation, combining in a single script the authorization and consumption of UserInfo :
PHP

  1. <?php
  2. /*
  3. testopenid2.php
  4.  
  5. OpenID Connect test :
  6. Register on this server a test application,
  7. Fill in the constants below accordingly,
  8. Launch this script: http://oa.dnc.global/oidc/testopenid2.php
  9.  
  10. Author :
  11. Bertrand Degoy https://degoy.com
  12. Credits :
  13. bschaffer https://github.com/bshaffer/oauth2-server-php
  14.  
  15. Licence : MIT licence
  16. Copyright (c) 2016 - DnC
  17.  
  18.  
  19. */
  20.  
  21. ini_set('display_errors', 1);
  22.  
  23. $client_id = 'testopenid';
  24. $client_secret = 'thesecret';
  25. //$redirect_uri = '';
  26.  
  27. $authorization_endpoint = 'http://oa.dnc.global/authorize';
  28. $token_endpoint = 'http://oa.dnc.global/token';
  29. $userinfo_endpoint = 'http://oa.dnc.global/userinfo';
  30.  
  31.  
  32. if (isset($_GET['error']))
  33. {
  34.     exit("Error: {$_GET['error']}. Description: {$_GET['error_description']}");
  35. }
  36. else if (isset($_GET['code']) && isset($_GET['state']))
  37. {
  38.     // Step 2. Token request
  39.  
  40.     $code = $_GET['code'];
  41.     echo "Authorization Code is {$code}\n\n";
  42.  
  43.     $data = array(
  44.         'grant_type' => 'authorization_code',
  45.         'code' => $code,
  46.     );
  47.  
  48.     $h = curl_init($token_endpoint);
  49.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  50.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  51.     curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  52.     curl_setopt($h, CURLOPT_POST, true);
  53.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  54.     curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  55.     //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  56.  
  57.     $res = curl_exec($h);
  58.     if (!$res)
  59.         exit(curl_error($h));
  60.  
  61.     curl_close($h);
  62.     $res = json_decode($res, true);
  63.  
  64.     echo "Token Response:\n";
  65.     print_r($res);
  66.     echo "\n";
  67.  
  68.     // Here you should decode JWT token and check sign using server's public key
  69.     // $payload = Jwt::decode($response['id_token'], $this->serverPublicKey);
  70.  
  71.     // If Token Response is valid goto step 3
  72.     // Step 3. Get UserInfo
  73.     $access_token = $res['access_token'];
  74.  
  75.     $h = curl_init($userinfo_endpoint);
  76.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  77.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  78.     curl_setopt($h, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $access_token));
  79.     //curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  80.  
  81.     $res = curl_exec($h);
  82.     if (!$res)
  83.         exit(curl_error($h));
  84.  
  85.     curl_close($h);
  86.     $res = json_decode($res, true);
  87.  
  88.     echo "UserInfo Response:\n";
  89.     print_r($res);
  90. }
  91. else
  92. {
  93.     // Step 1. Authorization Code request
  94.  
  95.     $data = array(
  96.         'response_type' => 'code',
  97.         'client_id' => $client_id,
  98.         'state' => 'xyz',
  99.         'scope' => 'openid profile',    
  100.     );
  101.  
  102.     $authorization_endpoint .= '?' . http_build_query($data);
  103.     header('Location: ' . $authorization_endpoint);
  104.     exit();
  105. }
  106. ?>

Télécharger

Run the script :https://oa.dnc.global/oidc/tests/te...

On the OAuthSD server, you can login with login = bebert and password = 012345678

A rigorous and therefore more complete version is given here : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.

Verification Key for Code Exchange (PKCE)

  publié le par DnC

OAuthSD can implement the Authorization code + PKCE flow. The main difference between the Authorization code + PKCE flow and the standard flow is that the client does not provide the application secret, but a verification key.

On the other hand, since there is no longer a secret, there is no longer any identification of the application implemented by the client, which is therefore of the "public" type.
The method avoids propagating a secret, but does not provide any new information on the identity of the application since it is a public instance that is thus identified. Conformity of the application to the original model is not guaranteed.

PKCE (pronounced "pixy") is an acronym for "Proof Key for Code Exchange".
See RFC 7636 : Proof of Key for Code Exchange (PKCE)

Usefulness of PKCE for mobile/native apps

When authenticating, mobile/native apps (without a backend) can use the authorization code flow, but they require additional security because they cannot Securely store the client secret that could be revealed by reverse-engineering (the client secret is tied to the application and is the same for all users and devices).
Apps without a backend can use a custom URL scheme to capture redirects (for example, MyApp ://). This can be hijacked by a malicious application, residing on the same user-agent, to receive an authorization code.

To address this issue, OAuth 2.0 and OpenID Connect provide a complement to the authorization code flow using a Verification Key for Code Exchange (PKCE).

Authorization code flow + PKCE

A client application implementing the authorization code flow with PKCE must be of the "public" type, i.e. be registered on the server with a null secret.

The public client application creates a random (unique and containing no information) string value, the code_verifier. When the client application initiates the authorization code request, instead of the secret, it sends to the Authorize controller the code_verifier hash under the code_challenge parameter.

After the user is authenticated and the authorization code is returned to the client application, the client application requests the tokens in exchange for the authorization code, including the code_verifier parameter.
If the codes match, authentication is complete and an access_token is returned.

Creation of the code_challenge

In the authorization code request, the application must transmit the code_challenge and the coding method "code_challenge_method" [1].

Starting from a random string ’code_verifier’, we obtain the code_challenge by applying a SHA256 hash and base64 URL encoding. You must then pass code_challenge_method = ’S256’

PHP

  1.   // Prepare PKCE code_challenge
  2.          $code_verifier = substr( str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEF"), 0, 50);
  3.          $code_challenge = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_');
  4.          $_SESSION['code_verifier'] = encrypt($code_verifier);
  5. ...

Télécharger

Javascript

  1. function base64URLEncode(str) {
  2.      return str.toString('base64')
  3.          .replace(/\+/g, '-')
  4.          .replace(/\//g, '_')
  5.          .replace(/=/g, '');
  6. }
  7. var code_verifier = crypto.randomBytes(50);
  8. var code_challenge = base64URLEncode(code_verifier);
  9. ...

Télécharger

ENFORCE_PKCE configuration constant

If the ENFORCE_PKCE configuration constant is set to true, all clients must enforce PKCE.
If false, PKCE is applied only to clients querying Authorize with the code_challenge parameter.
This constant is set to False by default.

Error ’missing_code_challenge’

If the ENFORCE_PKCE configuration constant is set to true or if the client queries Authorize with the code_challenge parameter, and the client does not provide the challenge code and encoding method in the authorization code request, the Authorize controller responds with a code 400 and the message ’missing_code_challenge’, ’This application requires you provide a PKCE code challenge’.

Call to Token controller

When the client application calls the Token controller to obtain the tokens, it passes the code_verifier parameter and the client ID in the body of the POST request (instead of passing the client ID and the secret in the header by the authentication method HTTP Basic).

Example in PHP :

  1.      $code_verifier = decrypt(@$_SESSION['code_verifier']);
  2.  
  3.      $data = array(
  4.          'client_id' => $client_id,
  5.          'grant_type' => 'authorization_code',
  6.          'code' => $code,
  7.          'code_verifier' => $code_verifier,
  8.      );
  9.  
  10.      $h = curl_init($token_endpoint);
  11.      curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12.      curl_setopt($h, CURLOPT_TIMEOUT, 10);
  13.      //curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  14.      curl_setopt($h, CURLOPT_POST, true);
  15.      curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  16.      curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  17.      curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  18.  
  19.      $res = curl_exec($h);
  20.      ...

Télécharger

Error ’code_verifier_missing’

If the client queried Authorize with a code_challenge (regardless of the value of the ENFORCE_PKCE configuration constant), and the client does not provide the ’code_verifier’ parameter in the token request, the Token controller responds with a 400 code and the message ’code_verifier_missing’, ’The PKCE code verifier parameter is required’.

PKCE Security Discussion

First of all, let’s remember that PKCE does not replace the secret of the application, and therefore does not provide any answer to the problem of identifying the client application.
The PKCE-enhanced authorization code flow introduces a random code "code_verifier", obscure (containing no information), created by the calling application and can be verified by the authorization server. Additionally, the calling application creates a transformed code checker value called "code_challenge" and sends this value over HTTPS to retrieve an authorization code. In this way, a malicious attacker can only intercept the authorization code and cannot exchange it for a token without the code checker.

However, after creating the verifier code in the first step of the authorization code flow, the client application must store it somewhere to provide it in the second step, when requesting the token. Even if this only lasts a very short time (of the order of a second), the code verifier is exposed to malware which clearly has everything it needs to try to obtain a token in place of the legitimate application. In principle, the legitimate application should have requested the token immediately, probably before the malware which will stumble on the ban on using the same verifier code twice. Unless the hacker is capable of changing execution priorities ? Even if it seems difficult or improbable, there is perhaps an opportunity for exploit.

Notes

[1code_challenge_method can take the values "plain" or "S256".