Performance testing angular/spring app secured with keycloak using Gatling (1/2).

piotr szybicki
12 developer labors
5 min readMay 5, 2018

--

Link to part 2.

This is the first part of two part series in it i will cover the testing, explanation and configuration of the two most widley used authorization flows ( described in OAuth 2.0 Spec) in the keycloack: ‘Authorization Code Flow’ and ‘Implicit Flow’ . Other parts of the stack are angular v5 and backend spring boot(1.5.3) rest application secured with spring security( keycloak extention). Link to the code in the section ‘Links’ bellow.

Now small theoretical introduction just to clear some of the confusion around ‘OpenID Connect’ and ‘OAuth 2.0’ standards because I can see a lot of blog post and YouTube videos that separate the two names with te ‘vs.’ word. I think that is a little bit misleading as the OpenID is just something provided on top of the OAuth. The OAuth protocol was designed just for delegated authorization (to check if you have a right to access a resource)but it became so successful that people started to use it for authentication as well (in SSO , mobile apps). There is no standardized way of getting user information. Different hacks begin to pop-up (facebook, google, twitter had their own mechanisms) forcing the page that wanted to use social login to adapt to all of them individually. As the answer to that challenge OpenID Connect spec was written, and standardized the authentication solution.

Now that we have some background story let’s jump in. In this tutorial i’m going to explain , and secure my app, using ‘Authorization Code Flow’ (In Keycloack it is called Standard Flow). Flow is just buch of GET and POST requests that application (client_id parametr) exchangea with the SSO provider.

A little bit of info on how the standard flow looks like. We go to the login page of the keycloak using bellow URL:

http://my.keycloack.com/auth/realms/poc/protocol/openid-connect/auth?client_id=app-user&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2F%3F&state=78fc552c-1168-4f59-9180-dfbb1f350767&nonce=4599a7aa-0399-41be-8c0d-c2514a98f11d&response_mode=fragment&response_type=code&scope=openid

This url has 4 parameters that are of special importance to us:

client_id: in keycloak we create that parameter ourselfs in Google SSO it is assigned for us. It is unique identifyer of our application. Type of flow we are going to use is specified for the client.

redirect_uri: the url to which we are going to be redirected after a successful login

response_type: we have code in our example and it indicated that we will get a some sort of id that we can use to fetch actual access token

scope: scope idicates to what resources we want to have access to. In my example we have openid (must if you are using OpenID Connect) and nothing else as in my example I disabled all restictions.

In the response we get the code that we can redeem for the set of tokens by doing a POST request to the appropriate endpoint:

http://my.keycloack.com/auth/realms/poc/protocol/openid-connect/token

In the response we get id_token, acccess_token, refresh_token and couple more usefull information.

we have to make access_token part of authorization header of the request that we are sending to back-end. But i’m getting ahead of myslef. Now for the application. I will not cover much how the demo is set up. As this article is more about the gatling test. But to setup the sample application you have to:

  1. You have to have keycloak-3.4.3.Final lunch it and then import realm-export.json file it will set up basic realm, client and user. Don’t change the port for me it was 8080
  2. Go to the folder frontend and type npm start it will lunch the angular app and the proxy server.
  3. Go to the folder backend and build using gradew.bat bootRepackage that will create the page
  4. Copy the file standalone.scal a that is in the folder additional to the ${gatling-folder}\user-files\simulations\ and run gutaling

Now for the explanation of the test, first we have to set up globla propertis. The only think worth noticing is the disableFollowRedirect it just gave me more controle over the execution of the test.

val httpProtocol = http
.baseURL("http://localhost:8080")
.inferHtmlResources()
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6")
.doNotTrackHeader("1")
.disableFollowRedirect
.userAgentHeader("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36")

Next we have to send the request for the login page and the perform the login. Here we can notice that i have to set the couple of parameters that i mentioned earlier redirect_uri, response_type, scope. Thay are required by the openid-connect protocol. After transformation of the response we can extract loginurl and use it in the next request. There we pass our username and password and from the responce, exctract the code that we can exckange for the access token.

.exec(http("get_login_page")
.get(authUri)
.queryParam("client_id", client_id)
.queryParam("redirect_uri", uri3)
.queryParam("state",UUID.randomUUID().toString())
.queryParam("nonce",UUID.randomUUID().toString())
.queryParam("response_mode", "fragment")
.queryParam("response_type", "code")
.queryParam("scope", "openid")
.headers(headers_3)
.check(status.is(200))
.check(css("#kc-form-login")
.ofType[Node]
.transform(variabe => { variabe.getAttribute("action") })
.saveAs("loginurl"))
)
.exec(http("login")
.post("${loginurl}")
.headers(headers_3)
.formParam("username", "user")
.formParam("password", "user")
.check(status.is(302))
.check(header("Location")
.transform( t=>{
t.substring(t.indexOf("code=") + 5 , t.length())
})
.saveAs("code"))
.check(header("Location").saveAs("nextPage"))
)

and we do it in the next request

.exec(http("fetch_token")
.post(tokenUri)
.headers(json_headers)
.header("Referer","uri3")
.formParam("code","${code}")
.formParam("grant_type","authorization_code")
.formParam("client_id", client_id)
.formParam("redirect_uri",uri3)
.check(status.is(200))
.check(jsonPath("$..access_token").saveAs("token"))
)

This is the good place to ask the question why not exchange our credentials directly for the token. Why add the extra step of fetching the code and then token. The anser is that if implemented correctly it is more secure. In my example the browser is aware of the tokens and we all now the browser is not as secure as application siting behind a firewall in some google/amazon datacenter. So in production solution what I would have to do is pass the code to my backend app that would exchange it for the token and to my browser would return just session_id or somthing like that. Now I know that as programmers we don’t like anything with the word state in it but trust me in security state it good. However if our application do not have a backend then the browser has to have access to tokens (and we would use the diffrent authentication flow) so it all boils down to the tradeoffs and design.

OK. that is all for this article. I put some usefull materials and the demo app bellow. If you have any questions or notice any errors ask in the comments and i will help or fix them.

Links

https://www.youtube.com/watch?v=996OiexHze0

https://www.youtube.com/watch?v=EDO1zlyFq6I

--

--

piotr szybicki
12 developer labors

Piotr Szybicki’s, Programmer, Java Developer, ML Entusiast