JMeter Basic Authentication Explained
You're often confronted with web application which requires user to authenticate. Basic Access Authentication is one of the most simple authentication method:
- Client includes an HTTP Header like
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
, with Base64 encoded username and password (username:password
equalsdXNlcm5hbWU6cGFzc3dvcmQ=
inBase64
) in each request, - Server grants access whenever the provided username and password are correct.
This guide will teach you 3 different ways to handle basic authentication:
- Using an HTTP Authorization Manager,
- Using a JSR223 PreProcessor with a custom script,
- Or using the
base64Encode
function from Custom Functions Plugin.
Let's see dive into JMeter with examples you can use yourself!
Example Application¶
Let's use HTTPBin.org as example application. We're interested in the following endpoints:
/basic-auth/:user/:passwd
Challenges HTTPBasic Auth./hidden-basic-auth/:user/:passwd
404'd BasicAuth./digest-auth/:qop/:user/:passwd/:algorithm
Challenges HTTP Digest Auth./digest-auth/:qop/:user/:passwd
Challenges HTTP Digest Auth.
HTTPBin is a demo application of great use: it provides sample endpoints to call with configurable parameters.
For example, when clicking on the /basic-auth/:user/:passwd
link, your browser (here I use Google Chrome) should prompt you to enter a username
and password
.
The default username is user
and the default password is passwd
. The server should respond with the following json:
{
"authenticated": true,
"user": "user"
}
What happens if we don't provide any credentials, or provide wrong credentials? Let's find out using Chrome Developer Tools. (hit F12
in Chrome to open it, then select Network
tab)
Shouldn't you provide the right username and password, the server rejects the access to the protected resource with an HTTP 401: Unauthorized error. See? There is no Authorization
header within the request.
When providing the right login and password, the server responds right away with an HTTP 200: OK
. And we can clearly see the browser sending the Authorization: Basic dXNlcjpwYXNzd2Q=
http header within the request.
Let's see how we can do this with JMeter!
Http Authorization Manager¶
Configuration¶
The key JMeter component to use is the HTTP Authorization Manager:
The Authorization Manager lets you specify one or more user logins for web pages that are restricted using server authentication.
It provides the ability to automatically add the relevant Authorization
http header to subsequent http requests.
Let's configure it to authenticate on HTTPBin.org https://httpbin.org//basic-auth/user/passwd
. Add the following line to the table:
- Clear Auth on each Iteration: can be left as is.
- Base URL:
https://httpbin.org/basic-auth/user/passwd
, - User:
user
, - Password:
passwd
, - Leave Domain and Realm empty,
- Mechanism:
BASIC_DIGEST
.
The HTTP Authorization Manager must be placed at the beginning of the thread group, right under it. The screen above shows the final configuration once done properly. Let's now create an HTTP Request!
HTTP Request¶
The HTTP Request is configured with the following settings:
- Protocol:
https
(http
is fine too, but must also be set in Authorization Manager accordingly), - Server name:
httpbin.org
, - Method: it's a simple
GET
request, - Path:
/basic-auth/user/passwd
.
All other settings can be left as is. Finally, we use the View Results Tree UI Listener to view the results.
We're now ready to execute the sample script!
Running The Test¶
First, I've ran the test with the HTTP Authorization Manager disabled. The server returned an HTTP 401 Unauthorized
as expected. Now let's enable it and compare the test runs.
Now the server grants the access and returns an HTTP 200 OK
response. That's great! A closer inspection of the request headers shows:
Request Headers:
Connection: keep-alive
Host: httpbin.org
User-Agent: Apache-HttpClient/4.5.5 (Java/1.8.0_161)
Authorization: Basic dXNlcjpwYXNzd2Q=
What about Digest Authentication? Unfortunately, JMeter doesn't support Digest Authentication yet. The authorization manager works great for Basic and Kerberos authentication thought.
The Www-Authenticate
header must therefore be generated manually using a script.
JSR223 PreProcessor¶
Configuration¶
You need to add the Authorization
header with the value Basic base64Encode(username:password)
. The problem is that JMeter has no base64 function. We're going to use the JSR223 Pre-Processor for that purpose. First, add the JSR223 PreProcessor.
Then, we're going to use the following script to encode the user:passwd
:
import org.apache.commons.codec.binary.Base64;
byte[] encodedUsernamePassword = Base64.encodeBase64("user:passwd".getBytes());
vars.put("auth",new String(encodedUsernamePassword));
Finally, we add an HTTP Header Manager right after the script.
The headers are configured as following:
- Name:
Authorization
, - Value:
Basic $[auth}
.
Results¶
Let's now run the test!
The script encoded the username and password combinations into a Base64 string, then put it in a variable. Finally, we reuse that variable within the header manager.
Custom Functions Plugin¶
Configuration¶
This is the third and last method to configure Basic Authentication: using the base64Encode
function from Custom Functions Plugin. First, you need to install The JMeter Custom Functions Plugin: See our JMeter Plugins Installation Guide for more information.
Open the Plugins Manager, then select Available Plugins
and type function
. The Custom JMeter Functions plugin should show up. Click on Apply changes and restart JMeter
to install it.
The ${__base64Encode(string)}
function is clearly what we need. This is why we installed the plugin: as seen previously, JMeter has no base64 function by default. Let's use it within the Authorization
header.
The headers are configured as following:
- Name:
Authorization
, - Value:
Basic ${__base64Encode(user:passwd)}
.
It's even easier to use than the JSR223 PreProcessor since you don't need an additional element!
JMeter Digest Auth¶
In order to execute an HTTP request against an endpoint which is protected by Digest Authentication, we need to use a JSR223 Sampler. Http Auth Manager doesn't support generating digest authentication headers by default. The server answered with an HTTP/1.1 200 OK
as expected!
The script is the following:
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
HttpHost target = new HttpHost("httpbin.org", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
try {
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local
// auth cache
DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name
digestAuth.overrideParamter("realm", "some realm");
// Suppose we already know the expected nonce value
digestAuth.overrideParamter("nonce", "whatever");
authCache.put(target, digestAuth);
// Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
HttpGet httpget = new HttpGet("https://httpbin.org/digest-auth/auth/user/passwd");
log.info("Executing request " + httpget.getRequestLine() + " to target " + target);
CloseableHttpResponse response = httpclient.execute(target, httpget, localContext);
try {
log.info("----------------------------------------");
log.info(String.valueOf(response.getStatusLine()));
log.info(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
It's an example script to HTTPBin https://httpbin.org/digest-auth/auth/user/passwd
. It creates a new HTTPClient and configures it properly to include the digest authentication header which should look like:
Authorization: Digest username="user", realm="me@kennethreitz.com", nonce="b8856184521cd0e23a3a8a8e04370158", uri="/digest-auth/auth/user/passwd/MD5/never", algorithm=MD5, response="1307ef4053cda82c0259f87656684d74", opaque="a5f3d832389217038bcadb09ef460895", qop=auth, nc=0000000a, cnonce="dc8bbb91c13c392d"
Once run, some of the information are logged within the JMeter Logs Panel. You should see something similar to:
2018-04-24 16:00:33,682 INFO o.a.j.p.j.s.JSR223Sampler: Executing request GET http://httpbin.org/digest-auth/auth/user/passwd HTTP/1.1 to target http://httpbin.org:80
2018-04-24 16:00:33,919 INFO o.a.j.p.j.s.JSR223Sampler: ----------------------------------------
2018-04-24 16:00:33,921 INFO o.a.j.p.j.s.JSR223Sampler: HTTP/1.1 200 OK
2018-04-24 16:00:33,922 INFO o.a.j.p.j.s.JSR223Sampler: {
"authenticated": true,
"user": "user"
}
That way you can even load test Digest Authentication protected web application. JMeter probably doesn't support it because it's not a common way to authenticate. You can now strut around the beach and impress the girls with your new JMeter skills!
Basic Authentication¶
Before diving into JMeter configuration, let's first understand how Basic Authentication works.
Don't fall asleep there, the nice things come after!
Old RFC2617¶
Basic authentication was initially based on RFC 2617. It stated the username and password should be encoded with ISO-8859-1
(also known as ASCII
) character encoding. Most servers understand it that way and fail to login when the charset is not appropriate.
Sadly, ISO-8859-1
character encoding, while being easy to use, has several flaws:
- You must use HTML Entities whenever the character is not included in the 189 chars supported,
- And only handles romance languages (mostly in Europe and America).
Wouldn't it be nice to use UTF-8
encoding then? That's exactly what RFC7617 is about.
New RFC7617¶
RFC7617 replaces RFC 2617 since 2015. The new RFC provides the ability to define the character encoding explicitely for the username and password.
Typically, the Authorization
header would look like:
Authorization: Basic BASE64, realm="octoperf.com", charset="utf-8"
In addition to this:
- Default Encoding is still
undefined
. It must be compatible with US-ASCII (which mapsASCII
bytes toUS-ASCII
bytes, likeUTF-8
), - Only UTF-8 alternative is allowed. Servers will accept
UTF-8
encoded username and password if the charset is specified.
What about the web browsers supporting this change?
Browser Support¶
As of now (presently 2018), most web browser (like Chrome, Firefox) all support the new RFC7617 with the following empirical results:
- Google Chrome seems to prefer
ÙTF-8
, - Microsoft Edge doesn't support UTF-8,
- Firefox since version 57 supports UTF-8.
That being said, the most commonly used web browser are all supporting the new specifications. We're done with the boring stuff!
Feel free to wander around and read our other JMeter tutorials. Also consider putting logins and passwords into CSV variables.