Skip to content

Instantly share code, notes, and snippets.

@gsandaru
Last active April 21, 2020 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gsandaru/8240cc62da84ae76bc0e94dc821a926e to your computer and use it in GitHub Desktop.
Save gsandaru/8240cc62da84ae76bc0e94dc821a926e to your computer and use it in GitHub Desktop.
OAuth2 Guide for Node Rest APIs and Android clients using KeyCloak Identity Server

OAuth2 Guide for Node Rest APIs and Android clients using KeyCloak Identity Server

What is KeyCloak ?

Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. It's open source and free to use

When to use KeyCloak ?

  • When you don't know how to implement complex role based user authentication/ authorization mechanism to your app.
  • Accelerated development time? maybe you'll have great business logic to focus on.

Few steps to complete !

  1. Setup and deploy KeyCloak Server. ( In 5 Mins ?😎😎 )
  2. Setup Realms and Clients.
  3. Configure Android App with AeroGear
  4. Configure NodeJS App with KeyCloak
  5. Connect Android Client to NodeJS API

1. KeyCloak in Heroku

Yes, We do this on free stuff. only free stuff... 😁 https://elements.heroku.com/buttons/mieckert/keycloak-heroku visit this and you'll see a beautiful button to deploy Configure an app name, username and password, hit save.

2. Setup KeyCloak - Realm

Realm is like an application context, You'll have many types of clients inside a realm, like an api will serve angular application as well as an android client. all three components will be in a single realm.

  • You'll notice a master realm that has been created for you

It is the highest level in the hierarchy of realms. Admin accounts in this realm have permissions to view and manage any other realm created on the server instance

  • For our application, we'll create another realm
    • Hover over "master" in sidebar panel ➡ click add realm ➡ type a name ➡ hit create
    • In Realm settings ( Menu ) ➡ Goto Login tab ➡ tick User Registeration, Login With Email and click Save.
    • In Clients ( Menu ), there will be serveal ClientIDs already created for you. click on account
      • add "oauth://oauth2callback" 🤷‍♂️( This is for Android Client )
      • change Access Type to Public
      • click Save.

3. Configure android client

Dependancies build.gradle(Module: app)

implementation ('org.jboss.aerogear:aerogear-android-authz:2.0.0') {  
        exclude module : 'compatibility-v4'  
        transitive = true  
    }

Android Manifest.xml file

<uses-permission android:name="android.permission.INTERNET"/>
    
    <service android:name="org.jboss.aerogear.android.authorization.oauth2.OAuth2AuthzService"/>
    
    <activity android:name=".MainActivity">  
	     <intent-filter> 
		     <action android:name="android.intent.action.MAIN" />   
		     <category android:name="android.intent.category.LAUNCHER" />  
		     <action android:name="android.intent.action.VIEW" />  
		     <category android:name="android.intent.category.DEFAULT" />  
		     <category android:name="android.intent.category.BROWSABLE" />  
      
		     <data  android:host="oauth2callback"  
			      android:scheme="oauth" />  
	     </intent-filter>
    </activity>

MainActivity.java ( onCreate )

try {  

	//Change -> testapp ( realm name ) and base URL
    AuthorizationManager.config("KeyCloakAuthz", OAuth2AuthorizationConfiguration.class)  
            .setBaseURL(new URL("https://your-app-name.herokuapp.com/auth"))  
            .setClientId("account") 
            .setAuthzEndpoint("/realms/testapp/protocol/openid-connect/auth")  
            .setAccessTokenEndpoint("/realms/testapp/protocol/openid-connect/token")  
            .setRefreshEndpoint("/realms/testapp/protocol/openid-connect/token")  
            .setAccountId("account")  
            .setRedirectURL("oauth://oauth2callback")  
            .asModule();  
  
  

    PipeManager.config("kc-upload", RestfulPipeConfiguration.class)  
                .module(AuthorizationManager.getModule("KeyCloakAuthz"))  
                .requestBuilder(new MultipartRequestBuilder());  
      
      authzModule = AuthorizationManager.getModule("KeyCloakAuthz");  
      
    } catch (MalformedURLException e) {  
        e.printStackTrace();  
    }

Let's create a button in MainActivity.xml and bind login method to click event.

    public void login(View view) {  
      
        authzModule.requestAccess(this, new Callback<String>() {  
            @Override  
		      public void onSuccess(String s) {  
	                //callback.onSuccess(s);  
		      Log.d(TAG, "Bearer Token :" + s);   
	      }  
      
              @Override  
	          public void onFailure(Exception e) {  
                if (!e.getMessage().matches(OAuthWebViewDialog.OAuthReceiver.DISMISS_ERROR)) {  
                    authzModule.deleteAccount();  
		      }  
            }  
        });  
    }

Save this token and you can use it in headers when calling NodeJS API

{ Authorization : { Bearer XXXXX }

4. Configure NodeJS App with KeyCloak

  1. Create a new client ➡ NodeAPI
  2. Access Type ➡ Public
  3. Valid Redirect URLs ➕ http://localhost:3000/*
  4. Goto Installation tab ➡ Select KeyCloak OIDC Json ➡ Download ( keycloak.json)

Create a new NodeJS Project

move keycloak.json file to root folder ( same folder where index.js is located ) and install following dependancies, run npm install

  • npm init -y
  • npm install express --save
  • npm install express-session --save
  • npm install keycloak-connect --save

Imports

	const session =  require("express-session");
    const Keycloak =  require("keycloak-connect");    
    const express =  require("express");    
    const app =  express(); 

Configurations

    // Configure session
    
    var memoryStore =  new  session.MemoryStore();    
    var keycloak =  new  Keycloak({ store: memoryStore });
    
    app.use(
      session({
        secret: "mySecret",
        resave: false,
        saveUninitialized: true,
        store: memoryStore
      })
    );
    
    // Attach middleware
    
    app.use(keycloak.middleware());

Public Route

    app.get("/", (req,  res,  next) => {    
	    res.json({ status:  "Public Endpoint" });    
    });

Protected Route

keycloak.protect() - middleware to check authenticated users

    app.get("/user/whoami", keycloak.protect(), (req, res, next) => {
      console.log(req.kauth.grant.access_token.content);
    
      let userinfo = { 
        name: req.kauth.grant.access_token.content.name, 
        email: req.kauth.grant.access_token.content.email, 
      };
    
      res.json(userinfo);
    });

app.listen(...) , you know this..

Open your browser and goto http://localhost:3000/user/whoami , you will be redirected to keycloak login, Create a new user and logIn to view user details as we implemented in Node route.

5. To Access NodeJS API from Android Client

After a success login, android client will receive a JWT Token from authzModule.requestAccess(..) call, save this token in runtime and use Retrofit or equalent http client including header { Authorization : Bearer a8sjc9ec.. }

public interface UserService {  
    @GET("/user/whoami")
    List<UserDetails> getUserDetails(@Header("Authorization") String bearerToken);
}

// BearerToken Should look like -> "Bearer WdNQ3dUQ..."

And we're done... it's time to explore role based authorization and more KeyCloak stuff.

I wrote this article on my first attempt with KeyCloak server, See any antipatterns ? want to add more to this article? Let me know!

Cheers 🥂

@MasterJEET
Copy link

MasterJEET commented Apr 21, 2020

add "oauth://oauth2callback" 🤷‍♂️( This is for Android Client )
--- Add where?

@gsandaru
Copy link
Author

add "oauth://oauth2callback" 🤷‍♂️( This is for Android Client )
--- Add where?

Android Manifest.xml file - > creates a deep link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment