§OpenID Support in Play
OpenID is a protocol for users to access several services with a single account. As a web developer, you can use OpenID to offer users a way to log in using an account they already have, such as their Google account. In the enterprise, you may be able to use OpenID to connect to a company’s SSO server.
§The OpenID flow in a nutshell
- The user gives you his OpenID (a URL).
- Your server inspects the content behind the URL to produce a URL where you need to redirect the user.
- The user confirms the authorization on his OpenID provider, and gets redirected back to your server.
- Your server receives information from that redirect, and checks with the provider that the information is correct.
Step 1 may be omitted if all your users are using the same OpenID provider (for example if you decide to rely completely on Google accounts).
§Usage
To use OpenID, first add openId
to your build.sbt
file:
libraryDependencies ++= Seq(
openId
)
Now any controller or component that wants to use OpenID will have to declare a dependency on the OpenIdClient.
§OpenID in Play
The OpenID API has two important functions:
OpenIdClient.redirectURL
calculates the URL where you should redirect the user. It involves fetching the user’s OpenID page asynchronously, this is why it returns aCompletionStage<String>
. If the OpenID is invalid, the returnedCompletionStage
will be completed with an exception.OpenIdClient.verifiedId
inspects the current request to establish the user information, including his verified OpenID. It will do a call to the OpenID server asynchronously to check the authenticity of the information, returning a promise of UserInfo. If the information is not correct or if the server check is false (for example if the redirect URL has been forged), the returnedCompletionStage
will be completed with an exception.
If theCompletionStage
fails, you can define a fallback, which redirects back the user to the login page or return aBadRequest
.
§Example
conf/routes
:
GET /openID/login controllers.OpenIDController.login()
POST /openID/login controllers.OpenIDController.loginPost()
GET /openID/callback controllers.OpenIDController.openIDCallback()
Controller:
import java.util.*;
import java.util.concurrent.CompletionStage;
import play.data.*;
import play.libs.openid.*;
import play.mvc.*;
import javax.inject.Inject;
public class OpenIDController extends Controller {
@Inject
OpenIdClient openIdClient;
@Inject
FormFactory formFactory;
public Result login() {
return ok(views.html.login.render(""));
}
public CompletionStage<Result> loginPost() {
// Form data
DynamicForm requestData = formFactory.form().bindFromRequest();
String openID = requestData.get("openID");
CompletionStage<String> redirectUrlPromise =
openIdClient.redirectURL(openID, routes.OpenIDController.openIDCallback().absoluteURL(request()));
return redirectUrlPromise
.thenApply(Controller::redirect)
.exceptionally(throwable ->
badRequest(views.html.login.render(throwable.getMessage()))
);
}
public CompletionStage<Result> openIDCallback() {
CompletionStage<UserInfo> userInfoPromise = openIdClient.verifiedId();
CompletionStage<Result> resultPromise = userInfoPromise.thenApply(userInfo ->
ok(userInfo.id() + "\n" + userInfo.attributes())
).exceptionally(throwable ->
badRequest(views.html.login.render(throwable.getMessage()))
);
return resultPromise;
}
public static class views {
public static class html {
public static class login {
public static Html render(String msg) {
return javaguide.ws.html.login.render(msg);
}
}
}
}
}
§Extended Attributes
The OpenID of a user gives you his identity. The protocol also supports getting extended attributes such as the e-mail address, the first name, or the last name.
You may request optional attributes and/or required attributes from the OpenID server. Asking for required attributes means the user cannot login to your service if he doesn’t provide them.
Extended attributes are requested in the redirect URL:
Map<String, String> attributes = new HashMap<>();
attributes.put("email", "http://schema.openid.net/contact/email");
CompletionStage<String> redirectUrlPromise = openIdClient.redirectURL(
openID,
routes.OpenIDController.openIDCallback().absoluteURL(request()),
attributes
);
Attributes will then be available in the UserInfo
provided by the OpenID server.