Scripting examples on how to use OAuth authentication in your load test.
OAuth authentication
The following examples take a set of arguments, shown in the function documentation, and returns the response body as JSON so that you can extract the token from.
Azure Active Directory
azure.js
1import http from 'k6/http';2
3/**4 * Authenticate using OAuth against Azure Active Directory5 * @function6 * @param {string} tenantId - Directory ID in Azure7 * @param {string} clientId - Application ID in Azure8 * @param {string} clientSecret - Can be obtained from https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-app#create-a-client-secret9 * @param {string} scope - Space-separated list of scopes (permissions) that are already given consent to by admin10 * @param {string} resource - Either a resource ID (as string) or an object containing username and password11 */12export function authenticateUsingAzure(tenantId, clientId, clientSecret, scope, resource) {13 let url;14 const requestBody = {15 client_id: clientId,16 client_secret: clientSecret,17 scope: scope,18 };19
20 if (typeof resource == 'string') {21 url = `https://login.microsoftonline.com/${tenantId}/oauth2/token`;22 requestBody['grant_type'] = 'client_credentials';23 requestBody['resource'] = resource;24 } else if (25 typeof resource == 'object' &&26 resource.hasOwnProperty('username') &&27 resource.hasOwnProperty('password')28 ) {29 url = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;30 requestBody['grant_type'] = 'password';31 requestBody['username'] = resource.username;32 requestBody['password'] = resource.password;33 } else {34 throw 'resource should be either a string or an object containing username and password';35 }36
37 const response = http.post(url, requestBody);38
39 return response.json();40}
Azure B2C
The following example shows how you can authenticate with Azure B2C using the Client Credentials Flow.
This example is based on a JMeter example found at the azure-ad-b2c/load-tests repository.
To use this script, you need to:
- Set up your own Azure B2C tenant
- Copy the tenant name, it will be used in your test script.
- Register a web application
- Register a single page application with the redirect URL of: https://jwt.ms. That's needed for the flow to receive a token.
- After the creation, you can get the Application (client) ID, and the Directory (tenant) ID. Copy both of them, they'll be used in your test script.
- Create a user flow so that you can sign up and create a user
- Create a new user, and copy the username and password. They'll be used in the test script.
If you missed copying any of the settings, you can find them in the B2C settings in the Azure portal.
azure-b2c.js
1import http from "k6/http";2import crypto from "k6/crypto";3import { randomString } from "https://jslib.k6.io/k6-utils/1.2.0/index.js";4
5const B2cGraphSettings = {6 B2C: {7 client_id: "", // Application ID in Azure8 user_flow_name: "",9 tenant_id: "", // Directory ID in Azure10 tenant_name: "",11 scope: "openid",12 redirect_url: "https://jwt.ms",13 },14};15
16/**17 * Authenticate using OAuth against Azure B2C18 * @function19 * @param {string} username - Username of the user to authenticate20 * @param {string} password21 * @return {string} id_token22 */23export function GetB2cIdToken(username, password) {24 const state = GetState();25 SelfAsserted(state, username, password);26 const code = CombinedSigninAndSignup(state);27 return GetToken(code, state.codeVerifier);28}29
30/**31 * @typedef {object} b2cStateProperties32 * @property {string} csrfToken33 * @property {string} stateProperty34 * @property {string} codeVerifier35 *36 */37
38/**39 * Get the id token from Azure B2C40 * @function41 * @param {string} code42 * @returns {string} id_token43 */44const GetToken = (code, codeVerifier) => {45 const url =46 `https://${B2cGraphSettings.B2C.tenant_name}.b2clogin.com/${B2cGraphSettings.B2C.tenant_id}` +47 `/oauth2/v2.0/token` +48 `?p=${B2cGraphSettings.B2C.user_flow_name}` +49 `&client_id=${B2cGraphSettings.B2C.client_id}` +50 `&grant_type=authorization_code` +51 `&scope=${B2cGraphSettings.B2C.scope}` +52 `&code=${code}` +53 `&redirect_uri=${B2cGraphSettings.B2C.redirect_url}` +54 `&code_verifier=${codeVerifier}`;55
56 const response = http.post(url, "", {57 tags: {58 b2c_login: "GetToken",59 },60 });61
62 return JSON.parse(response.body).id_token;63};64
65/**66 * Signs in the user using the CombinedSigninAndSignup policy67 * extraqct B2C code from response68 * @function69 * @param {b2cStateProperties} state70 * @returns {string} code71 */72const CombinedSigninAndSignup = (state) => {73 const url =74 `https://${B2cGraphSettings.B2C.tenant_name}.b2clogin.com/${B2cGraphSettings.B2C.tenant_name}.onmicrosoft.com` +75 `/${B2cGraphSettings.B2C.user_flow_name}/api/CombinedSigninAndSignup/confirmed` +76 `?csrf_token=${state.csrfToken}` +77 `&rememberMe=false` +78 `&tx=StateProperties=${state.stateProperty}` +79 `&p=${B2cGraphSettings.B2C.user_flow_name}`;80
81 const response = http.get(url, "", {82 tags: {83 b2c_login: "CombinedSigninAndSignup",84 },85 });86 const codeRegex = '.*code=([^"]*)';87 return response.url.match(codeRegex)[1];88};89
90/**91 * Signs in the user using the SelfAsserted policy92 * @function93 * @param {b2cStateProperties} state94 * @param {string} username95 * @param {string} password96 */97const SelfAsserted = (state, username, password) => {98 const url =99 `https://${B2cGraphSettings.B2C.tenant_name}.b2clogin.com/${B2cGraphSettings.B2C.tenant_id}` +100 `/${B2cGraphSettings.B2C.user_flow_name}/SelfAsserted` +101 `?tx=StateProperties=${state.stateProperty}` +102 `&p=${B2cGraphSettings.B2C.user_flow_name}` +103 `&request_type=RESPONSE` +104 `&email=${username}` +105 `&password=${password}`;106
107 const params = {108 headers: {109 "X-CSRF-TOKEN": `${state.csrfToken}`,110 },111 tags: {112 b2c_login: "SelfAsserted",113 },114 };115 http.post(url, "", params);116};117
118/**119 * Calls the B2C login page to get the state property120 * @function121 * @returns {b2cStateProperties} b2cState122 */123const GetState = () => {124 const nonce = randomString(50);125 const challenge = crypto.sha256(nonce.toString(), "base64rawurl");126
127 const url =128 `https://${B2cGraphSettings.B2C.tenant_name}.b2clogin.com` +129 `/${B2cGraphSettings.B2C.tenant_id}/oauth2/v2.0/authorize?` +130 `p=${B2cGraphSettings.B2C.user_flow_name}` +131 `&client_id=${B2cGraphSettings.B2C.client_id}` +132 `&nonce=${nonce}` +133 `&redirect_uri=${B2cGraphSettings.B2C.redirect_url}` +134 `&scope=${B2cGraphSettings.B2C.scope}` +135 `&response_type=code` +136 `&prompt=login` +137 `&code_challenge_method=S256` +138 `&code_challenge=${challenge}` +139 `&response_mode=fragment`;140
141 const response = http.get(url, "", {142 tags: {143 b2c_login: "GetCookyAndState",144 },145 });146
147 const vuJar = http.cookieJar();148 const responseCookies = vuJar.cookiesForURL(response.url);149
150 const b2cState = {};151 b2cState.codeVerifier = nonce;152 b2cState.csrfToken = responseCookies["x-ms-cpim-csrf"][0];153 b2cState.stateProperty = response.body.match('.*StateProperties=([^"]*)')[1];154 return b2cState;155};156
157/**158 * Helper function to get the authorization header for a user159 * @param {user} user160 * @returns {object} httpsOptions161 */162export const GetAuthorizationHeaderForUser = (user) => {163 const token = GetB2cIdToken(user.username, user.password);164
165 return {166 headers: {167 "Content-Type": "application/json",168 Authorization: "Bearer " + token,169 },170 };171};172
173export default function () {174 const token = GetB2cIdToken("zimmi.94@live.de", "20B2cTest23");175 console.log(token);176}
Okta
Okta-test.js
1import http from 'k6/http';2import encoding from 'k6/encoding';3
4/**5 * Authenticate using OAuth against Okta6 * @function7 * @param {string} oktaDomain - Okta domain to authenticate against (e.g. 'k6.okta.com')8 * @param {string} authServerId - Authentication server identifier (default is 'default')9 * @param {string} clientId - Generated by Okta automatically10 * @param {string} clientSecret - Generated by Okta automatically11 * @param {string} scope - Space-separated list of scopes12 * @param {string|object} resource - Either a resource ID (as string) or an object containing username and password13 */14export function authenticateUsingOkta(15 oktaDomain,16 authServerId,17 clientId,18 clientSecret,19 scope,20 resource21) {22 if (authServerId === 'undefined' || authServerId == '') {23 authServerId = 'default';24 }25 const url = `https://${oktaDomain}/oauth2/${authServerId}/v1/token`;26 const requestBody = { scope: scope };27 let response;28
29 if (typeof resource == 'string') {30 requestBody['grant_type'] = 'client_credentials';31
32 const encodedCredentials = encoding.b64encode(`${clientId}:${clientSecret}`);33 const params = {34 auth: 'basic',35 headers: {36 Authorization: `Basic ${encodedCredentials}`,37 },38 };39
40 response = http.post(url, requestBody, params);41 } else if (42 typeof resource == 'object' &&43 resource.hasOwnProperty('username') &&44 resource.hasOwnProperty('password')45 ) {46 requestBody['grant_type'] = 'password';47 requestBody['username'] = resource.username;48 requestBody['password'] = resource.password;49 requestBody['client_id'] = clientId;50 requestBody['client_secret'] = clientSecret;51
52 response = http.post(url, requestBody);53 } else {54 throw 'resource should be either a string or an object containing username and password';55 }56
57 return response.json();58}
For a detailed example, please visit this article: How to Load Test OAuth secured APIs with k6?