Friday, June 29, 2018

Server to Server communication with Okta authorization server, Spring Boot & OAuth 2.0

When you need to integrate two servers with authentication, you can use Client Credentials Flow in Okta along with Spring Boot and OAuth 2.0. This Client Credential Flow has been recommended for machine to machine authentication. The application that being build is need to securely store its ClientID and Secret with Okta pass in exhange for an access token. Actually, the authentication flow has only two steps, the application needs to pass client credentials to the Okta Authorization server and then if the credentials are true, Okta will responds with an access token.
Let's see how to create this Okta account and configure the authorization server.

 
Set up your authorization server
Okta can be used as an Authorization server for implementing client credentials flow. To create a Okta account, you can sign up to Okta using their official website
  
Keep a note on the okta URL which is shown after signed up to okta.

Then, you will receive an email from Okta with a temporary password.


Click on the Sign In button and sign in with the username and temporary password. After that, enter the new password and other information and click on Create My Account

Click on the application tab the okta dashboard and click Add Application button to add a new application.


Select on Service machine to machine button and click Next

Give a name to the service and then you will see the application details.


You will need these Client Credential details in the next steps.
And you need to create a custom scope since Client Credential never has a user context
That’s why you must create a custom scope.
Creating a custom scope
Go to the API tab in the Okta dashboard and select Authorization Servers.
Then click on edit icon.

Click on the Scope tab and click on Add Scope button to add a scope. Give a name and description for the scope and click Create button.



Now you can check the authorization server /token endpoint using postman by sending a request with Basic Auth.
curl --request POST \
  --url https://dev-945334.oktapreview.com/oauth2/default/v1/token \
  --header 'accept: application/json' \
  --header 'authorization: Basic MG9hY...' \
  --header 'cache-control: no-cache' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=client_credentials&redirect_uri=http%3A%2F%2Flocalhost%3A8080&
  scope=customScope'

Postman Authorization tab

Postman header tab

Postman body tab


When you send the request to the Authorization Server, it will respond the following access token if the credentials are accurate.
{
    "access_token": "eyJraWQiOiJQaXFRUlktMnROdzVfSUVyNWkwVGlmVjJ4X19jMXNUd2JqbEpDVUdhdG84IiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULi1vSjZ1M2FzSEotcXdXUENFUC1NbUcxcjdlbFNTYU5IRkxrSHJoeHN2ZU0iLCJpc3MiOiJodHRwczovL2Rldi05NDUzMzQub2t0YXByZXZpZXcuY29tL29hdXRoMi9kZWZhdWx0IiwiYXVkIjoiYXBpOi8vZGVmYXVsdCIsImlhdCI6MTUzMDI0ODY4NSwiZXhwIjoxNTMwMjUyMjg1LCJjaWQiOiIwb2FmbTdsOWowSUVkTmx3aTBoNyIsInNjcCI6WyJjdXN0b21TY29wZSJdLCJzdWIiOiIwb2FmbTdsOWowSUVkTmx3aTBoNyJ9.Fb_PkFNHmtn1Iwz9vIe8u2ngBw1qzkwqaiuOI0oJOmztjPxl9u5niqc2cgthU7bB5SUBVVqYpodRoDaIOMrjBwIaPNCnOKuFiRoJtQ2C1tDTwUT9ENSgbUQwBFs3fznr8s75W5MCdmIn21mlM9nsKnKbAGXfhYcMBjFWu8v9d-ZE38TkYMh1opjp0AvpNaBgU5qiRd335CrDm3Rv4Yx5CmX5J4cO97Dc68FDK-sFFaIRuNjgUxNjyS5fSx5lNxm9NCPloTXySDhpZeRvqMPMYPGpStEi0rCGpC-zrvbYy7DJOuMA5_Nf-yHMxUaDUYZrjdMZArhm3qN9J9b97LYAaA",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "customScope"
}

To implement sample client credential application, I will create a Spring Boot server application and Client application.
The server application which is a resource server (API Service) is a simple and consist of a single /mod end point.

Create Resource server app
Create Spring Boot project with security and web as dependencies and then add one more dependency to the pom.xml file
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
Add server application to include @EnableResourceServer annotation and add a simple Rest Controller

@EnableResourceServer
@SpringBootApplication
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }
    /**
     * Allows for @PreAuthorize annotation processing.
     */
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
        @Override
        protected MethodSecurityExpressionHandler createExpressionHandler() {
            return new OAuth2MethodSecurityExpressionHandler();
        }
    }
@RestController
    public class MessageOfTheDayController {
        @GetMapping("/mod")
        @PreAuthorize("#oauth2.hasScope('custom_mod')")
        public String getMessageOfTheDay(Principal principal) {
            return "Hello, Good morning: " + principal.getName();
        }
    }
 
Then rename application.properties file to application.yml and update it as follows
security:
  oauth2:
    client:
      clientId: {client-id-from-above}
      clientSecret: {client-secret-from-above}
    resource:
      tokenInfoUri: {issuer-uri-from-above}/v1/introspect
Now you can run the server application and you can try to access http://localhost:8080/mod.
it will respond with a, HTTP 401 UNAUTHORIZED

Create the Client App
Next, you create a simple command line app (or API service), so create a Spring Boot project with security dependency and oauth dependency to the pom.xml as we did to the resource server.
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
Rename application.properties to application.yml and add the following info
example:
  oauth2:
    client:
      grantType: client_credentials
      clientId: {client-id-from-above}
      clientSecret: {client-secret-from-above}
      accessTokenUri: {issuer-uri-from-above}/v1/token
      scope: custom_mod
server:
  port : 8081 

Here, I configured a few properties:
baseUrl is the base URL of our example server
grantType defines the grant type for the connection
clientId and clientSecret are the same as above
accessTokenUri defines the URI used to get an access token
scope is the custom scope we created above
In Demo Application,
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
     private final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
     @Value("#{ @environment['example.baseUrl'] }")
    private String serverBaseUrl; 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    } 
    @Bean
    @ConfigurationProperties("example.oauth2.client")
    protected ClientCredentialsResourceDetails oAuthDetails() {
        return new ClientCredentialsResourceDetails();
    } 
    @Bean
    protected RestTemplate restTemplate() {
        return new OAuth2RestTemplate(oAuthDetails());
    } 
    @Override
    public void run(String... args) {
        logger.info("MOD: {}", restTemplate().getForObject(serverBaseUrl + "/mod", String.class));
    }
}
Note:
·       The CommandLineRunner interface adds run method that will automatically called once the application initialized. And the application will be exited after this method.
·       ClientCredentialsResourceDetails bean is bound to configuration properties: example.oauth2.client
·       OAuth2RestTemplate in place of a standard RestTemplate this automatically manages all the OAuth 2.0 access token exchange and sets the Authentication: Bearer header value. Basically, it handles all the OAuth details.
Now you can run the client application and see the console output similar to below message
2018-06-29 11:43:20.396  INFO 63788 --- [           main] com.nishantha.demo.DemoApplication       : MOD: Hello, Good morning: 0oafls01eedXspzFG0h7



 

No comments:

Post a Comment