Skip to content

GJWT/javaOIDCMessage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

javaOIDCMessage

NOTE: Work in progress. Part of the source code is copied from https://github.com/GJWT/java-jwt/tree/secondPR.

Introduction

The OpenID Connect (OIDC) and OAuth2 standards define request and response messages - requests that are sent from clients to servers and responses from servers to clients.

This library allow us to:

  • Instantiate and modify OIDC and OAuth2 messages from scratch or set of claims
  • Verify the validity of the messages as well as the correctness of the claims that they contain
  • Serialize the message into the correct on-the-wire representation (e.g. Json, URL encoded, JWT)
  • Deserialize a received message from the on-the-wire representation (e.g. Json, URL encoded, JWT)

Basic usage

All the messages implement the interface org.oidc.msg.Message. The OAuth2 -specific messages can be found from the org.oidc.msg.oauth2 package, as OIDC -specific messages are located at the org.oidc.msg.oidc package.

The messages can be constructed from scratch and the claims added one by one:

import org.oidc.msg.oidc.AuthenticationRequest;

...

	AuthenticationRequest message = new AuthenticationRequest();
	message.addClaim("response_type", "code");
	message.addClaim("client_id", "s6BhdRkqt3");
	message.addClaim("state", "xyz");
	message.addClaim("redirect_uri", "https://client.example.com/cb");

...
	

Or an use existing Map can be exploited to provide the claims for the message constructor:

import java.util.HashMap;
import java.util.Map;
import org.oidc.msg.oidc.AuthenticationRequest;

...

	Map<String, Object> claims = new HashMap<>();
	claims.put("response_type", "code");
	claims.put("client_id", "s6BhdRkqt3");
	claims.put("state", "xyz");
	claims.put("redirect_uri", "https://client.example.com/cb");
	AuthenticationRequest message = new AuthenticationRequest(claims);

...
	

Serialization / deserialization

Since all the messages are used in an environment where information are to be sent over a wire it must be possible to serialize the information in such an instance to a format that can be transmitted over-the-wire.

Because of this a number of method has been added to support serialization to and deserialization from a number of representations that are used in the OAuth2 and OIDC protocol exchange.

The following three formats are supported:

  • JSON
  • URL-encoded as defined in RFC 3986
  • JSON Web Token (JWT) signed and/or encrypted.

An example using URL encoding:

import org.oidc.msg.oidc.AuthenticationRequest;

...

	AuthenticationRequest message = new AuthenticationRequest();
	message.addClaim("response_type", "code");
	message.addClaim("client_id", "s6BhdRkqt3");
	message.addClaim("state", "xyz");
	message.addClaim("redirect_uri", "https://client.example.com/cb");
	System.out.println(message.toUrlEncoded());
...
	
?response_type=code&state=xyz&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&client_id=s6BhdRkqt3

Another example using JSON:

import java.util.Arrays;
import org.oidc.msg.oidc.RegistrationRequest;

...

	RegistrationRequest message = new RegistrationRequest();
	message.addClaim("redirect_uris", Arrays.asList("https://client.example.com/cb", "https://client.example.com/cb2"));
	message.addClaim("response_types", Arrays.asList("code"));
	message.addClaim("application_type", "web");
	System.out.println(message.toJson());
...
	
{"application_type":"web","redirect_uris":["https://client.example.com/cb","https://client.example.com/cb2"],"response_types":["code"]}

And finally example using JWT, where also a Key is needed for signing the JWT:

import java.util.Arrays;
import java.util.Date;
import org.oidc.msg.oidc.IDToken;

...

    IDToken message = new IDToken();
    message.addClaim("iss", "https://issuer.example.com");
    message.addClaim("aud", Arrays.asList("https://client.example.com"));
    message.addClaim("sub", "mockSubject");
    message.addClaim("iat", new Date());
    Key key = new SYMKey("sig", "mockSecret");
    System.out.println(message.toJwt(key, "HS256"));

...
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IiJ9.eyJhdWQiOiJodHRwczovL2NsaWVudC5leGFtcGxlLmNvbSIsInN1YiI6Im1vY2tTdWJqZWN0IiwiaXNzIjoiaHR0cHM6Ly9pc3N1ZXIuZXhhbXBsZS5jb20iLCJpYXQiOjE1MzQ5NTc2Mjd9.k-3sS2TYxHwmmKfi3HI80IEDe8MadJDjWJIFc_MJVXc

Verifying the message content

A protocol specification would not be anything if it didn’t specify what a message is supposed to look like. Which attributes that can occur in a message and what type of values the attributes could have. And in some extreme case the specification can also specify the exact values that a specific attribute can have.

The OAuth2 and OpenID Connect specifications does all that. But both of them also states that extra attributes can always occur and should be allowed.

All messages extending org.oidc.msg.AbstractMessage class can deal with this. Let’s make a basic error response message as an example. This message is defined as follows:

import org.oidc.msg.AbstractMessage;
import org.oidc.msg.ParameterVerification;

public class ErrorMessage extends AbstractMessage {

  { // Set parameter requirements for message.
    paramVerDefs.put("error", ParameterVerification.SINGLE_REQUIRED_STRING.getValue());
    paramVerDefs.put("error_description", ParameterVerification.SINGLE_OPTIONAL_STRING.getValue());
    paramVerDefs.put("error_uri", ParameterVerification.SINGLE_OPTIONAL_STRING.getValue());
  }

...

What this means is that error must have a java.lang.String value and that error_description and error_uri may have values and if so single java.lang.String values.

The following values for org.oidc.msg.ParameterVerification are provided by the library off the shelf:

  • SINGLE_(OPTIONAL or REQUIRED)_STRING: Value as java.lang.String.

  • SINGLE_(OPTIONAL or REQUIRED)_INT: Value as java.lang.Long or java.lang.Integer or numeric java.lang.String which are converted into java.lang.Long.

  • SINGLE_(OPTIONAL or REQUIRED)_BOOLEAN: Value as java.lang.Boolean or java.lang.String containing true or false, which are converted into java.lang.Boolean.

  • SINGLE_(OPTIONAL or REQUIRED)_DATE: Value as java.util.Date or int, long, java.lang.Integer or java_lang.Long containing epoch seconds, which are converted into java.util.Date.

  • SINGLE_(OPTIONAL or REQUIRED)_MAP: Value as java.util.Map with java.lang.String as key format.

  • (OPTIONAL or REQUIRED)_LIST_OF_STRINGS: Value as java.util.List with java.lang.String format, or single non-empty java.lang.String which are converted into java.util.List.

  • (OPTIONAL or REQUIRED)_LIST_OF_SP_SEP_STRINGS: Value as space-separated java.lang.String or in non-empty array of _java.lang.String_s which are converted into space-separated java.lang.String.

  • SINGLE_(OPTIONAL or REQUIRED)_MESSAGE: Value as any class implementing org.oidc.msg.Message interface. The message is not verified.

  • SINGLE_(OPTIONAL or REQUIRED)_JWT: Value as java.lang.String that can be decoded into com.auth0.JWT. The JWT is not otherwise verified.

The messages can be verified using the verify() method, see below:

...

	ErrorMessage message = new ErrorMessage();
	message.addClaim("error", "invalid_request");
	System.out.println(message.verify());

...
true

As the only required attribute (error) is correctly defined the verify() method will evaluate to true. If we forget to provide the error attribute, the method will evaluate to false and the details for error can be contained via getError() method:

...

	ErrorMessage message = new ErrorMessage();
	message.addClaim("error_description", "Some strange error");
	if (!message.verify()) {
		System.out.println(message.getError().getDetails());
	}

...
[parameterName=error, errorType=MISSING_REQUIRED_VALUE, errorMessage=null, errorCause=null]

The Error class contains a list of ErrorDetails, containing details for the incorrect parameter/attribute name and the error type. Optionally also the error message and the cause Throwable may be included in the details.

In addition to the desired value format of the parameters, the allowed values can set in the following way:

import java.util.Arrays;
import org.oidc.msg.AbstractMessage;
import org.oidc.msg.ParameterVerification;

public class ErrorMessage extends AbstractMessage {

  { // Set parameter requirements for message.
    paramVerDefs.put("error", ParameterVerification.SINGLE_REQUIRED_STRING.getValue());
    paramVerDefs.put("error_description", ParameterVerification.SINGLE_OPTIONAL_STRING.getValue());
    paramVerDefs.put("error_uri", ParameterVerification.SINGLE_OPTIONAL_STRING.getValue());
    allowedValues.put("error", Arrays.asList("invalid_request", "unauthorized_client")); 
  }

...

This way only the two values invalid_request or unauthorized_client are allowed for the error claim. See:

...

    ErrorMessage message = new ErrorMessage();
    message.addClaim("error", "invalid_error");
    if (!message.verify()) {
      System.out.println(message.getError().getDetails());
    }

...
[parameterName=error, errorType=VALUE_NOT_ALLOWED, errorMessage=null, errorCause=null]

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages