BreadcrumbHomeResourcesBlog How To Run Performance Tests On OAuth Secured Apps With JMeter November 18, 2020 How to Run Performance Tests on OAuth Secured Apps with JMeter Open Source AutomationPerformance TestingBy Dmitri TikhanskiIn this blog we will cover JMeter OAuth testing including why it's important, and three options on how to run performance tests on OAuth secured apps with JMeter.Table of ContentsWhat is OAuth & Why Do You Need it?Why and How Developers Started Using OAuth3 Ways to Run JMeter Performance Tests on OAuth Secured AppsBottom LineTable of Contents1 - What is OAuth & Why Do You Need it?2 - Why and How Developers Started Using OAuth3 - 3 Ways to Run JMeter Performance Tests on OAuth Secured Apps4 - Bottom LineBack to topWhat is OAuth & Why Do You Need it?OAuth (the Open Standard for Authorization) is an open protocol which provides token-based authentication and authorization - as opposed to the standard username and password requirements. It allows third party services to use the end-user information without revealing their personal credentials. Here’s an example of how it works:The user goes to website A.On website A, he’s given the option to sign in by using website B (Google, Facebook, LinkedIn, GitHub, etc.).The user confirms that information obtained from website B (i.e. first name and last name) can be used by website A.The user is known on website A by the credentials (i.e. first name and last name) that he set up on website B.This entire process is known as the flow. As you can see, the end user doesn’t need to enter his credentials at all on website A, which makes it far more secure.Back to topWhy and How Developers Started Using OAuthOAuth version 1.0 was first released in 2007. Twitter/X was the first platform to start using OAuth to access its API. By 2010, the platform had made the use of OAuth compulsory for all 3rd-party applications working with its API. In the same year, IETF launched OAuth 2.0. Like its predecessors (Oauth 1.0 and OAuth 1.0.a), OAuth 2.0 gives users the ability to grant third-party access to web resources without sharing their passwords.New features in OAuth 2.0 include new flows, simplified signatures and short-lived tokens with long-lived authorizations.OAuth has a huge number of flows, different protocol versions - with opposing viewpoints on them (the creator of OAuth 1.0 withdrew his name and support from OAuth 2.0 due to suspected security flaws) - and various server/client sides implementations (OAuth 2.0 leaves a lot of things up to the implementor).Back to top3 Ways to Run JMeter Performance Tests on OAuth Secured AppsLet's explore three options of running JMeter performance tests on OAuth secured apps.Option 1: The End UserThis first option is the closest one to a real-life user experience (logging into website A with your credentials from website B). For this test scenario, I’m going to use blazemeter.com which provides the possibility to use your existing Google account instead of signing up. When you click the button, it takes you to your Google Account sign-in page where you provide your email and password and returns you to the BlazeMeter application authenticated and authorized. I’m going to use:setUp Thread GroupWebDriver Sampler plugin to open BlazeMeter and Google and store the cookies into JMeter Properties Normal “Thread Group” withHTTP Cookie Manager - to manage cookies.JSR223 PreProcessor - to convert WebDriver cookies into JMeter Cookies.HTTP Request sampler - to send the request to BlazeMeter application - it should bypass any login pages and open the dashboard directly.The WebDriver Sampler Groovy code, which opens BlazeMeter via a Google account looks like:import org.openqa.selenium.By import org.openqa.selenium.support.ui.ExpectedConditions import org.openqa.selenium.support.ui.WebDriverWait WDS.sampleResult.sampleStart() WebDriverWait wait = new WebDriverWait(WDS.browser, 30L) WDS.browser.get('https://blazemeter.com') def startTestingNow = wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector('a[class="try-for-free"]'))) startTestingNow.click() def signIn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath('//a[text()="Sign In"]'))) signIn.click() def signInWithGoogle = wait.until(ExpectedConditions.elementToBeClickable(By.xpath('//span[text()="Sign In with Google"]'))) signInWithGoogle.click() def usernameInput = wait.until(ExpectedConditions.elementToBeClickable(By.id('identifierId'))) usernameInput.sendKeys(WDS.vars.get('username')) def nextButton = wait.until(ExpectedConditions.elementToBeClickable(By.id('identifierNext'))) nextButton.click() def passwordInput = wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector('input[type="password"]'))) passwordInput.sendKeys(WDS.vars.get('password')) nextButton = wait.until(ExpectedConditions.elementToBeClickable(By.id('passwordNext'))) nextButton.click() wait.until(ExpectedConditions.urlContains('a.blazemeter.com/app')) def cookies = WDS.browser.manage().getCookies() WDS.props.put('cookies', cookies) WDS.props.put('URL', WDS.browser.getCurrentUrl()) WDS.sampleResult.sampleEnd()And the code for the JSR223 PreProcessor is:import org.apache.jmeter.protocol.http.control.Cookie def cookies = props.get('cookies') sampler.setPath(props.get('URL')) cookies.each { cookie -> sampler.getCookieManager().add(new Cookie(cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath(), cookie.isSecure(), cookie.getExpiry()?.getTime() ?: System.currentTimeMillis() + 86400000L)) }Test plan overview and outcome in the View Results Tree listener:The JMeter .jmx file can be found in the Bitbucket repository, plus: The script uses WebDriver Sampler plugin so make sure to install it using JMeter Plugins Manager.Download chromedriver for your Chrome version.Change email and password in the User Defined Variables to your very own ones.Run the test.You should be able to get the same results. Option 2: On OAuth 1.0OAuth 1.0 has a 3-step authorization flow to accessing protected resources. These are:Obtaining the Request Token and Token Secret using the Consumer Key and Consumer Secret Obtaining the Authorization Token using the Request Token and Secret Exchanging the Authorization Token to the Access Token - which can be used to access protected resources. Any of the above requests (and, in fact, any request for protected resources) must be signed using one of the following methods: PLAINTEXT HMAC-SHA1 RSA-SHA1Put simply, the request parameters will look something like this: oauth_consumer_keyOAuth 1.0 Consumer Keyoauth_tokenAccess token from above step 3oauth_signature_methodOne of above request signing methodsoauth_timestampCurrent timestamp (in seconds from (01/01/1970)oauth_nonceAny random string (usually current timestamp in milliseconds from 01/01/1970)any other request parameters oauth_signaturecalculated depending on the signature typeThe most tricky part is the oauth_signature. Here’s how you do it for each method. PLAINTEXT: URL-encoded Consumer Secret + & + Token SecretHMAC-SHA1: base64 of the sha1 of the Signature Base String - the HTTP Method followed by "&", then the URL of the resource (http or https), and then followed by the parameters which are sent to the endpoint and sorted alphabetically - hashed by the Consumer SecretRSA-SHA1: PKCS#1 of Signature Base String hashed by Consumer’s private RSA key - and again Base64 and URL-encoded. Given the complexity of building this request, it’s best to use one of the OAuth Java Client Libraries. In this demo, I’ll be using the:oauth-signpost - as the OAuth 1.0 client library.http://oauthbin.com/ - as the sample OAuth1 test server.The right Test Element to be used when developing the OAuth 1.0 client-side code is the JSR223 Sampler with Groovy.Here is the test flow:Download Groovy, find the groovy-all.jar and drop it into JMeter’s /lib folderDo the same for the latest version of the oauth-signpost jar for the /lib folder of your JMeter installation. As for signpost-core-1.2 and JMeter 2.11, the signpost-core.jar should be enough as other dependencies are already part of JMeter. Restart JMeter (if it’s running)Add a Thread Group to the Test PlanAdd User Defined Variables to the Thread Group or Test Plan. Provide the following variables:consumerKey - keyconsumerSecret - secretrequestUrl - http://oauthbin.com/v1/request-tokenaccessUrl - http://oauthbin.com/v1/access-tokenprotectedUrl - http://oauthbin.com/v1/echoAdd the JSR223 Sampler to the Thread GroupType “groovy” into the “Language” inputProvide the following script:import oauth.signpost.OAuth; import oauth.signpost.OAuthConsumer; import oauth.signpost.OAuthProvider; import oauth.signpost.basic.DefaultOAuthConsumer; import oauth.signpost.basic.DefaultOAuthProvider; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; OAuthConsumer consumer = new DefaultOAuthConsumer(vars.get("consumerKey"), vars.get("consumerSecret")); URL url = new URL(vars.get("requestUrl")); HttpURLConnection request = (HttpURLConnection) url.openConnection(); consumer.sign(request); request.connect(); BufferedReader reader = new BufferedReader(new InputStreamReader( request.getInputStream())); String inputLine; log.info("========== OAuth token and token secret =========="); while ((inputLine = reader.readLine()) != null) log.info(inputLine); reader.close(); request.disconnect(); OAuthProvider provider = new DefaultOAuthProvider( vars.get("requestUrl"), vars.get("accessUrl"), vars.get("protectedUrl")); log.info("========== OAuth Request Token =========="); log.info(provider.retrieveRequestToken(consumer, OAuth.OUT_OF_BAND)); provider.retrieveAccessToken(consumer, OAuth.OUT_OF_BAND); url = new URL(vars.get("protectedUrl") + "?jmeter=great"); request = (HttpURLConnection) url.openConnection(); consumer.sign(request); request.connect(); reader = new BufferedReader(new InputStreamReader( request.getInputStream())); log.info("========== Authenticated Request Response =========="); while ((inputLine = reader.readLine()) != null) log.info(inputLine); reader.close(); request.disconnect(); Enable the Log Viewer from the “Options” menu.Run the test and look into the log window. The output should look like the image below:Option 3: OAuth 2.0 OAuth 2.0 is considered simpler and easier than OAuth 1.0/1.0a. It doesn’t require any crazy signatures, timestamps or secrets - and for the simulation, all you have to do is add an HTTP Header Manager as a child of a single request, or at the same level as all requests (depending on the desired scope). Only one header is enough to access protected resources:Name: AuthorizationValue: Bearer ${OAuth2Token}Without OAuth2Token, you’ll need to either implement the end-user scenario (i.e. Option 1) or take a harder route.Just like for OAuth 1.0, you’ll need the following libraries to be in the /lib folder of your JMeter installation:groovy-all-x.jar (downloadable from http://groovy.codehaus.org/Downloadgoogle-api-client-1.19.0.jargoogle-api-services-plus-v1-rev137-1.19.0.jargoogle-http-client-1.19.0.jargoogle-http-client-jackson2-1.19.0.jargoogle-oauth-client-1.19.0.jarguava-14.0.jarguava-jdk5-13.0.jarjackson-core-2.1.3.jarjsr305-1.3.9.jarThe Google Plus OAuth2 authentication script looks like this (add the code below to the JSR223 Sampler):import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.plus.Plus; import com.google.api.services.plus.PlusScopes; import com.google.api.services.plus.model.Activity; String APPLICATION_NAME = vars.get("applicationName"); String SERVICE_ACCOUNT_EMAIL = vars.get("serviceAccountEmail"); JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport) .setJsonFactory(JSON_FACTORY) .setServiceAccountId(SERVICE_ACCOUNT_EMAIL) .setServiceAccountScopes(Collections.singleton(PlusScopes.PLUS_ME)) .setServiceAccountPrivateKeyFromP12File(new File(vars.get("p12FilePath"))) .build(); Plus plus = new Plus.Builder(httpTransport, JSON_FACTORY, credential) .setApplicationName(APPLICATION_NAME).build(); String activityId = vars.get("activityId"); Activity activity = plus.activities().get(activityId).execute(); log.info("id: " + activity.getId()); log.info("url: " + activity.getUrl()); log.info("content: " + activity.getObject().getContent()); Now, let’s take a look at the User Defined Variables that need to be set and see where to get the appropriate values. We’ll need the following:applicationName - the name of the product which will perform the OAuth authentication and authorization.serviceAccountEmail - Google Service Account Email Address.p12FilePath - location of .p12 file containing Google Service Account credentials.activityId - Google+ activity ID - protected resource, can be left default - z12gtjhq3qn2xxl2o224exwiqruvtda0i.You need to have a Google Account to get the relevant values for points 1-3: Go to the Google Developer Console at: https://console.developers.google.com/project.Click the “Create Project” button and provide a Name and ID.Expand “API and Auth” and go to “Consent Screen”.Fill out the “Email Address” and “Product Name” fields.Go to “APIs”.Turn on “Google+ API”.Go to “Credentials” and click “Create new Client ID”.Choose “Service Account” (otherwise you’ll need to provide a redirect URL and use the browser for Google Authentication). See the first section for more information on this.After clicking the “Create” button, you’ll receive a .p12 key file and password. Save it somewhere so JMeter can access it (i.e. in the /bin folder of your JMeter installation)You’ll be able to see your Service Email Address and Client ID in the “Credentials” screen.Populate the User Defined Variables with the values from your Google Developer Console and run your test. Now look at the Log Viewer window: And last but not least, it is possible to open an OAuth-protected application without using any third-party libraries or browsers. You can just correlate dynamic values using JMeter’s built-in test elements. Back to topBottom LineIn this blog, we covered the key ways you can access JMeter OAuth-protected resources. If you’re already using BlazeMeter (or considering using it), it is important to note that it supports all of the approaches mentioned above.Experience BlazeMeter for yourself. Start testing today with your free trial. START TESTING NOWRelated Resources:Security Testing With JMeter - Learn HowBack to top
Dmitri Tikhanski Contributing Writer Dmitri Tikhanski is a Contributing Writer to the BlazeMeter blog.