How to generate OAuth 2.0 token via karate.
How we have tried in Postman:
Postman then redirects to a browser where we enter username and password and once authenticated it redirects user back to postman with access token.
Question:
When we provide grant_type as "authorization code" in Karate we are getting an error as {"error":"unsupported_grant_type","error_description":"Unsupported grant_type"}. What to provide here as when we provide "password" we are getting 401 and when we provide "authorization code" we are getting 400.
Secondly, Can we automate such scenario where a browser is invoked as well and we have to enter credentials can we achieve it via Karate as then we have to store the token and pass in the APIs?
Background:
* url 'http://localhost:8080/pathdetails'
Scenario: get all users and then get the first user by id
* path 'token'
* form field grant_type = 'authorization code'
* form field client_id = 'ourapiclient'
* form field client_secret = '324243324-3334-334-343-3432423424'
* method post
* status 200
* def accessToken = response.access_token
EDITED**********
I have now tried to send a API request to Auth URL which redirects to the browser and returns HTML page.
Given url 'http://localhost:8080/myurlpath/auth'
* form field response_type = 'code'
* form field client_id = 'abcc'
* form field scope = 'openconnect'
* form field redirect_uri = 'http://localhost:8080/redirecturlpath'
* form field state = 'cEY3R-YfsoM9232diS72COdHTA8uPv9K49pjZaPag5M.8akinzwobn8.abcd4'
* method get
* status 200
* print 'Response is........',response
This returned an HTML page which is exactly the same page I see when I send request from Postman. How to now enter username and password in karate on this html page as this page was returned as part of the response of above API.
I was expecting above will return me a code and after that I will call the request token endpoint but above redirected me to where I enter username and password and then once it is successful it redirects back to Postman and in URL I can see the code as well.
curl --request POST \
--url 'https://YOUR_DOMAIN/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data 'client_id=YOUR_CLIENT_ID' \
--data client_secret=YOUR_CLIENT_SECRET \
--data code=YOUR_AUTHORIZATION_CODE \
--data 'redirect_uri=https://YOUR_APP/callback'
How to get the code which is needed by the token API?
I tried sending Auth API to access like below but no code or token got returned in the response.
Given driver 'http://localhost:8080/myurlpath/auth?scope=openconnect&state=cEY3R-YfsoM9232diS72COdHTA8uPv9K49pjZaPag5M.8akinzwobn8.abcd4&response_type=code&client_id=abcc&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Fmyurlpath'
* fullscreen()
And input('#username', 'username')
And input('#password', 'password')
When click('#login')
The above doesn't return any error but it doesn't return the code I am looking for as well
Here my solution for creating access token OAuth 2.0 with PKCE
Feature: OAuth 2.0 Authentication with Authorization Code grant type and PKCE
Background:
* url 'https://aaa.amazoncognito.com/oauth2'
* def scope = 'openid profile'
* def callbackURL = 'https://aaa.com'
* def grantType = 'authorization_code'
* configure ssl = true
* def ac = Java.type('utilities.AuthCode')
* def newClass = new ac()
* def cV = newClass.generateCodeVerifier()
#* print 'code verify= '+ cV
* def cC = newClass.generateCodeChallenge(cV)
#* print 'code chal= '+ cC
* def sleep = function(millis){ java.lang.Thread.sleep(millis) }
* def delay = 1000
Scenario: Get Access Token with PKCE
Given path 'authorize'
* configure followRedirects = false
* form field response_type = 'code'
* form field client_id = clientID
* form field client_secret = clientSecret
* form field scope = 'openid profile'
* form field redirect_uri = 'https://aaa.com'
* form field code_challenge = cC
* form field code_challenge_method = 'S256'
* method get
* status 302
* def location = responseHeaders['Location'][0]
#auth code
* configure driver = { type: 'chrome', addOptions: ["--remote-allow-origins=*"] }
Given driver location
* sleep(delay)
And input('/html/body/div[1]/div/div[2]/div[2]/div[2]/div[2]/div/div/form/div[1]/input', username)
And input('/html/body/div[1]/div/div[2]/div[2]/div[2]/div[2]/div/div/form/div[2]/input', password)
When click('/html/body/div[1]/div/div[2]/div[2]/div[2]/div[2]/div/div/form/input[3]')
* def actualUrl = waitForUrl('?code')
* def authCode = actualUrl.substring(actualUrl.lastIndexOf('=') + 1)
* driver.quit()
#final
* configure followRedirects = true
Given url 'https://aaa.amazoncognito.com/oauth2/token'
* header Content-Type = 'application/x-www-form-urlencoded'
* form field grant_type = 'authorization_code'
* form field client_id = clientID
* form field client_secret = clientSecret
* form field code_verifier = cV
* form field code = authCode
* form field redirect_uri = 'https://aaa.com'
* method post
* status 200
* match response.token_type == 'Bearer'
* def id_token = response.id_token
* def access_token = response.access_token
* def refresh_token = response.refresh_token
#* print 'access token is = '+ access_token
For AuthCode java class is
package utilities;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class AuthCode {
/**
* This method generates a secure string used as the PKCE code verifier value.
* In PKCE, you use the code verifier value:
* 1. When you generate a code challenge value.
* 2. When you exchange an authorization code for a bearer JWT token.
* @return A random code verifier value.
*/
final public String generateCodeVerifier() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[36];
secureRandom.nextBytes(bytes);
String result = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
//System.out.println("code verifier = " + result);
return result;
}
/**
* This method generates a code challenge value by SHA-256 and base64 encoding the code verifier value.
* In PKCE, you use the code challenge value when you construct the authorization request uri.
* @param codeVerifierValue A random string value.
* @return An encoded code challenge string value.
* @throws NoSuchAlgorithmException
*/
public String generateCodeChallengeHash(String codeVerifierValue) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] codeVerifierBytes = codeVerifierValue.getBytes(StandardCharsets.US_ASCII);
byte[] digest = messageDigest.digest(codeVerifierBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
}
public String generateCodeChallenge(String cV){
// System.out.println("code verifier in cC method = " + cV);
String result;
try {
result = generateCodeChallengeHash(cV);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
//System.out.println("generated Code Challenge = " + result);
return result;
}
}