123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- [[recipe-securing-a-client-application-with-oauth]]
- = Recipe: Securing a Client Application with OAuth
- This section describes how to create an OAuth client application.
- NOTE: We use "`OAuth`" and "`OAuth2`" interchangeably. Spring Security has moved away from the first version of OAuth.
- [[understanding-oauth]]
- == Understanding OAuth
- Another way to secure an application is with OAuth. According to one https://oauth.net/[OAuth web site], OAuth is "`An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications.`"
- To understand OAuth, you should first understand the difference between authentication and authorization.
- Authentication is the process of proving that someone is who they say they are.
- Authentication is typically done with a login screen that prompts for a user name and password (though it can be done by other means, such as fingerprint readers).
- Authorization is the process of granting permission to do something.
- Authorization does not (with one notable exception, which we cover later in this section) involve authentication.
- In the typical OAuth authorization scenario, a third-party application wants authorization to do some action on another application, and it wants authorization to do whatever that action may be.
- Consider the following common scenario. An application wants to add content (perhaps news about a certain subject) to your Facebook feed.
- The application that wants to add news stories is the third-party application, and the end user can authorize Facebook to let that happen.
- In this scenario (and many similar scenarios), we see three roles:
- * User: The person who wants to get news stories added to their Facebook page.
- * Client: The third-party application that has the news stories.
- * Resource Server: Facebook's API that can grant authorization.
- Another part of the process is the authorization server, which is the resource server's user interface that lets the Facebook user say OK to those news stories being added.
- For small applications, the resource server and the authentication server may be the same thing. For larger applications, the authorization server is a separate application.
- To make things more clear, consider the flow of actions in the process to which we have so far alluded in this section:
- . The user finds a web application that offers to add news stories to their Facebook page and thinks that is a fine idea.
- . The user clicks a button to make that happen.
- . The news application (the client in this scenario) redirects to Facebook's authorization server.
- The user may have to log in to the resource server (Facebook, in this case) at this point, to verify their identity.
- . The user clicks an Accept (or similar) button to tell Facebook to grant permission to the news application.
- . The user returns to the news application, which now has permission to add news stories to the user's Facebook page.
- The whole process is not complicated, but it is substantially different than the security scenarios we have covered in other recipes, notably logging in may not be required.
- Again, we deal with authorization rather than authentication.
- === OAuth and Redirect URIs
- OAuth applications do not accept redirects from just anywhere.
- Before such a redirect can work, the third-party (client) application must register with the resource server application (Facebook in our example).
- Then the resource server knows that redirects from the client application are OK.
- === The Client ID and the Secret
- When the user in the example we described earlier authorizes the client (the news service in our example), the resource server (Facebook in our example) sends a client ID to the client and may also send a secret to the client.
- The client ID is public and identifies the user for the client application (the news service).
- The secret (if it is sent) makes it easy for the client to complete transactions (sending news stories to the user's Facebook page).
- ==== When to Not Send Secrets
- If the client cannot keep a secret, the resource server should not send a secret.
- That happens for single-page web applications (such as the Angular application from the <<recipe-securing-an-angular-web-application>> recipe) and for mobile applications.
- Because bad actors can get the secret in those situations, the resource server should not send a secret to mobile and single-page applications.
- Instead, the resource server and client can use a secret that is generated for each request.
- One standard for doing so is https://oauth.net/2/pkce/[PKCE] (Proof Key for Code Exchange).
- PKCE exists "`to prevent certain attacks and to be able to securely perform the OAuth exchange from public clients.`"
- For the resource server, the trick is knowing when to send a secret and when to use PKCE.
- This can be determined when the client registers with the resource server.
- == Writing the Client Application
- NOTE: This section, including the application, was adapted from a Spring Security example at https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2login. You can find many other Spring Security examples in the project that contains this application.
- To create a client application that accesses Facebook, you must:
- . <<oauth-adding-a-new-application,Add a new application>>
- . Configure application.yml
- . Boot up the application
- [[oauth-adding-a-new-application]]
- === Adding a New Application
- To use Facebook’s OAuth 2.0 authentication system for login, you must first https://developers.facebook.com/apps[Add a New App].
- To do so from Facebook's New App page:
- . Select *Create a New App*
- +
- The "Create a New App ID" page appears.
- . Enter the *Display Name*, *Contact Email*, and *Category*
- +
- NOTE: The selection for the *Category* field is not relevant, but the field is requred. Select "Local" for its value.
- . Click *Create App ID*.
- +
- The next page to appear is "Product Setup".
- . Click the *Get Started* button for the Facebook Login product.
- . In the left sidebar, under *Products → Facebook Login*, select *Settings*.
- . For the field Valid OAuth redirect URIs, enter `http://localhost:8080/login/oauth2/code/facebook`.
- +
- For a real application, you would almost certainly have a URI that did not use `localhost`.
- However the sample shown in this recipe uses localhost, so that you can run it locally.
- . Click *Save Changes*.
- The OAuth redirect URI is the path in the application that the end-user’s user-agent is redirected back to after they have authenticated with Facebook and have granted access to the application on the Authorize application page.
- TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. The `registrationId` is a unique identifier for the `ClientRegistration`.
- IMPORTANT: If the application runs behind a proxy server, you should check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure that the application is correctly configured. Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[URI template variables] for `redirect-uri`.
- ==== Configuring the `application.yml`
- Now that you have created a new application with Facebook, you need to configure the sample application to use the application for the authentication flow. To do so:
- . Go to `application.yml` (in the `resources` directory) and set the following configuration:
- +
- ====
- ----
- spring:
- security:
- oauth2:
- client:
- registration: <1>
- facebook: <2>
- client-id: facebook-client-id
- client-secret: facebook-client-secret
- ----
- <1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth client properties.
- <2> The base property prefix follows the ID for the `ClientRegistration` -- in this case, `facebook`.
- ====
- . Replace the values in the client-id and client-secret property with the OAuth 2.0 credentials you created earlier.
- ==== Creating the Application
- The application consists of two small classes: a class with a main method that is annotated with `@SpringBootApplication` to create a Spring Boot application and an OAuth2 login controller. The following listing shows the application class:
- ====
- [source,java]
- ----
- package sample;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- /**
- * @author Joe Grandja
- */
- @SpringBootApplication
- public class OAuth2LoginApplication {
- public static void main(String[] args) {
- SpringApplication.run(OAuth2LoginApplication.class, args);
- }
- }
- ----
- ====
- The following listing shows the OAuth2 login controller class:
- ====
- [source,java]
- ----
- package sample.web;
- import org.springframework.security.core.annotation.AuthenticationPrincipal;
- import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
- import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
- import org.springframework.security.oauth2.core.user.OAuth2User;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.GetMapping;
- /**
- * @author Joe Grandja
- * @author Rob Winch
- */
- @Controller
- public class OAuth2LoginController {
- @GetMapping("/")
- public String index(Model model,
- @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,
- @AuthenticationPrincipal OAuth2User oauth2User) {
- model.addAttribute("userName", oauth2User.getName());
- model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());
- model.addAttribute("userAttributes", oauth2User.getAttributes());
- return "index";
- }
- }
- ----
- ====
- You can find the original source for these classes plus test classes in the Spring Security samples at https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2login.
- == OAuth Resources
- OAuth2 is defined by https://tools.ietf.org/html/rfc6749[IETF RFC (Request for Comment) 6749].
- Two highly regarded and closely related web sites offer more detail.
- Those sites are https://oauth.net/ and https://oauth.com/.
- https://oauth.net/ is organized as a wiki. https://oauth.com/ is organized as a book.
- Both are worth reading if you need to understand OAuth2 in depth.
|