Post Page Advertisement [Top]

A Basic Comprehensive Application to Understand OAuth2.0 Authorization Code Grant Type


Understand OAuth  Authorization Code Grant Type

Fork this project on GitHub
 https://github.com/mifrazmurthaja/oauth2.0-basic



Prerequisites


Project Structure
  • The project has 3 applications
    • Resource Server of wowelephant.lk - Runs on port 8082
      • localhost:8082
    • Authorization Server of wowelephant.lk - Runs on port 8081
      • localhost:8081
    • Client Application - Walapalam - Runs on port 9999
      • localhost:9999 
  • Project Structure is as follows
    • Resource Server of wowelephant.lk



ResourceServer.java
           
The main class of Resource Server.

User.java
           
The user class consists of attributes of the user and get-set methods.

UserData.java
Functions related to the users such as, addUser(), updateUser(), viewUser() and etc. In this case, we use files to store the data instead of Database.

application.yml
Configurations such as port number and the details of the Authorization Server.

Users.dat
The file where all the user details are stored.



Each of following functions has a scope, path and a request method. To access any of the functions, it requires a valid access token with the given request method (GET, POST, etc)

For example,
localhost:8082/friends requires an access token with the user_friend scope and it can be accessed only through GET method.

localhost:8082 (Root - /) will return “Welcome” if it is accessed through GET method with a valid token consists of public_read scope.


private String message = "Welcome!";

@PreAuthorize("#oauth2.hasScope('public_read')")
@RequestMapping(value = "/", method = RequestMethod.GET)
public Map<String, String> home() {
    return Collections.singletonMap("message", message);
}

@PreAuthorize("#oauth2.hasScope('public_write')")
@RequestMapping(value = "/", method = RequestMethod.POST)
public void updateMessage(@RequestBody String message) {
    this.message = message;
}

@PreAuthorize("#oauth2.hasScope('user_read')")
@RequestMapping(value = "/user", method = RequestMethod.GET)
public Map<String, String> user(Principal user) {
    return Collections.singletonMap("message", "user is: " + user.toString());
}

UserData userData = new UserData();

@PreAuthorize("#oauth2.hasScope('user_friend')")
@RequestMapping(value = "/friends", method = RequestMethod.GET)
public List<User> getUsers(){
    return userData.getAllUsers();
}

    • Authorization Server of wowelephant.lk
   

 AuthorizationServer.java
           
The main class of Authorization Server.

OAuthConfig.java
Configurations of OAuth framework such as Client IDs, Client Secrets, Token Validity periods, scopes and etc.

SecurityConfig.java
Defines how the Authorization Server can be accessed such as login page and user credentials. (Not the client credentials – Credentials of real users whose data is stored on the resource server)

application.properties
Configurations such as port number and the server context path.

jwt.jks
The key file. Password for the key is stored in the application.properties file.


The client details of walapalam application are stored in the following function. Since this application does not use a database, all the details are hardcoded. We can store multiple client details using and() method. The client 1000123456 can only request user_read or user_friend or both from users. And the authorization code will be sent to the redirection URI defined below. The authorization flow grant type used in this application is authorization code grant type.
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("1000123456")
            .authorizedGrantTypes("authorization_code")
            .scopes("user_read","user_friend")
            .redirectUris("http://localhost:9999/oauth/access/?key=value")
            .secret("mAac87WQq")
            .accessTokenValiditySeconds(5184000);
}

Below function defines the user credentials whose resource is stored on the resource server. (Hardcoded) 

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {   auth.parentAuthenticationManager(authenticationManager)
            .inMemoryAuthentication()
            .withUser("john")
            .password("123")
            .roles("USER");
}

    • Client Application - Walapalam
 


App.java
           
The main class of Client Application - Walapalam.

AppController.java
Define paths for the HTML static pages which are store on resources/static folder.

AppRestController.java
Defines paths for the functions.

application.yml
Configurations such as the port number.

index.html
Homepage (Login page) of the walapalam application. The path for this file is defined in AppController.java – Root path (/) – Hence, localhost:9999/ returns this page.


Path for the index.html is defined as root in AppController.java class
@RequestMapping("/")
public String index() {

    return "index.html";
}


A link button – Login with Wowelephant, which redirects the user to localhost:9999/login-wowelephant

<a href="/login-wowelephant" class="btn button btn-primary a-btn-slide-text">
    <span><strong>Login with WowElephant</strong></span>
</a>


If anyone accesses the /login-wowelephant path, following function will be triggered and will be redirected to the URL defined below. If you have a look at the URL, it clearly shows that
·         Port number is 8081. ie, the Authorization Server.
·         Client ID of the walapalam application.
·         Scopes (Separated by spaces – encoded value: %20)
·         The redirect URI. The endpoint, the authorization code will be sent to.
o   If you have look at the redirect URI, it is localhost:9999 and that is the Client App. Hence, the authorization code will be sent to the client app on /oauth/access path. Before sending the code to the client app, the user will be asked to approve the request by User Consent Page.
·         Response type – Authorization Code.

@RequestMapping(value = "/login-wowelephant", method=RequestMethod.GET)
public RedirectView processForm1() {
    RedirectView redirectView = new RedirectView();
    redirectView.setUrl("http://localhost:8081/auth/oauth/authorize?response_type=code&client_id=1000123456&redirect_url=http://localhost:9999/oauth/access?key=value&scope=user_friend%20user_read");
    return redirectView;
}


When the authorization code received to /oauth/access, the following method will be executed.
Now, we do all the rest of steps on this method itself. (From step 5 – 10)

@RequestMapping(value = "/oauth/access", method = RequestMethod.GET)
public String handleResponse(ModelMap model, @RequestParam(value = "code",required=true) String authCode) {

    String accessToken = getAccessToken(authCode); //Get the access token from the authorization server   
   return getResource(accessToken); //Get the resource from the resource server using the access token
}
 It has 2 functions,
·         getAccessToken() – Get the access token from the authorization server by providing the received authorization code. (Steps 5 & 6)
o   Method should be POST
o   Mention the Grant Type and the Authorization Code in the body.
o   Set the Authorization Header with value of encoded value in the form of
   Basic Client_ID:Client_Secret

·         getResource() – Get the resource of the user from the resource server by providing the access token.
o   Method can be any.
o   Access the required path depending on the resource to be accessed.
o   Set the Authorization Header with value in the form of
   Bearer Access_Token


public String getAccessToken(String authCode)
{
    String auth_url = "http://localhost:8081/auth/oauth/token";
    String POST_PARAMS = "grant_type=authorization_code&code="+authCode;
    String CLIENT_ID = "1000123456";
    String CLIENT_SECRET = "mAac87WQq";
    String accessToken = "";

    try    {
        URL obj = new URL(auth_url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        con.setRequestMethod("POST");

        String cred = CLIENT_ID+":"+CLIENT_SECRET;

        byte[] encodedCred = Base64.getEncoder().encode(cred.getBytes());
        //Set Headers       
        con.setRequestProperty("content-type", "application/x-www-form-urlencoded");
        con.setRequestProperty("Authorization", "Basic " + new String(encodedCred));

        //Set Body      
        con.setDoOutput(true);
        OutputStream os = con.getOutputStream();
        os.write(POST_PARAMS.getBytes());
        os.flush();
        os.close();


        //Execute and get the response      
         int responseCode = con.getResponseCode();

        if (responseCode == HttpURLConnection.HTTP_OK)//success       
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null)
            {
                response.append(inputLine);
            }
            in.close();

            //Convert the response to a json Object           
            JSONObject jsonObj = new JSONObject(response.toString());

            //Get the access token from json object           
            accessToken = jsonObj.getString("access_token");
        }
        else        {
            System.out.println("Error : " + responseCode);
        }
    }
    catch (Exception ex)
    {
        System.out.println(ex);
    }
    return accessToken;
}




public String getResource(String accessToken)
{
    try    {
        String ResourceUrl = "http://localhost:8082/friends";

        URL obj = new URL(ResourceUrl);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        con.setRequestMethod("GET");

        //Set Header       
        con.setRequestProperty("Authorization", "Bearer "+accessToken);

        //Execute and get the response       
        int responseCode = con.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) //success       
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine;
            StringBuffer ResourceResponse = new StringBuffer();

            while ((inputLine = in.readLine()) != null)
            {
                ResourceResponse.append(inputLine);
            }
            in.close();

            //Convert the ouput to json array           
            JSONArray jsonArray = new JSONArray(ResourceResponse.toString());

            //Prepare the ouput from the json array           
            String output = "<html><body><h2>Friends List</h2><table width=50% style=\"text-align:center\">";
            output += "<tr><th>ID</th><th>Name</th></tr>";
            for (int i = 0; i < jsonArray.length(); i++)
            {
                JSONObject explrObject = jsonArray.getJSONObject(i);
                output += "<tr>";
                output += "<td>" + explrObject.getString("id") + "</td>";
                output += "<td>" + explrObject.getString("name") + "</td>";
                output += "<tr>";
            }
            output += "</table></body></html>";
            return output;

        }
        else        {
            System.out.println("Error : " + responseCode);
        }
    }
    catch (Exception ex)
    {
        System.out.println(ex);
    }

    return null;
 






·        
 Resource Server cannot be accessed directly.

 


·         
 User clicks on Login with WowElephant



·         
 The user will be redirected to the login page of the Authorization Server since the user is not logged in. It will be requested each time since this application mainly focuses on the OAuth protocol hence sessions are not used.



·        
 User Consent Page.



·        
 Client app will be provided with an Authorization Code (Have a look at the below URL) after the user authorized the above request. After that, client app continues with the rest of the step to get the resource of the user displayed below.





How to run the project?

Open servers and application on 3 different terminals and execute the command, 
mvn spring-boot:run



While the servers are running, open the browser and go to localhost:9999
To stop the servers, enter ctrl + C 

Fork this project on GitHub
 https://github.com/mifrazmurthaja/oauth2.0-basic

 That's all folks!

Bottom Ad [Post Page]

| Designed by Colorlib