trying to write custom authentication script - openID/oauth2

417 views
Skip to first unread message

simon o

unread,
Aug 17, 2023, 11:17:00 AM8/17/23
to ZAP User Group
I am having trouble testing/debugging my zap script. It is using ECMAScript : Oracle Nashorn. It was really easy for me to develop a script in javascript, or python, but extremely challenging for me to do this within the zap scripting engine. I have another script that i started within, in plain javascript, that works when i execute it from node. I am not getting any error when i load the script, and it is printing out my debug statement. However, it never actually runs the authentication script no matter what configuration options i try. I generally have no problem configuring authentication except for with my own script.

Script:

var HttpRequestHeader = Java.type("org.parosproxy.paros.network.HttpRequestHeader");
var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader");
var URI = Java.type("org.apache.commons.httpclient.URI");
var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars');

print("in main function"); // <-- This is our debug print statement

var USERNAME = "";
var PASSWORD = "";
var AUTH_URL = "";
var OAUTH2_URL = "";
var CLIENT_ID = '';

function generateRandomString() {
var raw = Java.type("java.util.Base64").getEncoder().encodeToString(Java.type("java.security.SecureRandom").getInstanceStrong().generateSeed(48));
raw = raw.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
return raw.substring(0, 64);
}

function getSessionToken(helper) {
var body = JSON.stringify({
password: PASSWORD,
username: USERNAME,
options: {
warnBeforePasswordExpired: true,
multiOptionalFactorEnroll: false
}
});

var requestURI = new URI(AUTH_URL, false);
var requestMethod = HttpRequestHeader.POST;
var requestMainHeader = new HttpRequestHeader(requestMethod, requestURI, HttpHeader.HTTP11);

var msg = helper.prepareMessage();
msg.setRequestBody(body);
msg.setRequestHeader(requestMainHeader);
msg.getRequestHeader().setContentLength(msg.getRequestBody().length());

helper.sendAndReceive(msg, false);

var json = JSON.parse(msg.getResponseBody().toString());
return json.sessionToken;
}

function makeAuthorizedRequest(helper, sessionToken) {
var nonce = generateRandomString();
var state = generateRandomString();

var display = 'page';
var redirectUri = '';
var responseType = 'id_token token';
var scope = 'profile email openid';

var urlWithSessionToken = OAUTH2_URL + "?client_id=" + CLIENT_ID + "&display=" + display + "&nonce=" + nonce + "&redirect_uri=" + encodeURIComponent(redirectUri) + "&response_type=" + responseType + "&sessionToken=" + encodeURIComponent(sessionToken) + "&state=" + state + "&scope=" + scope;

var requestURI = new URI(urlWithSessionToken, false);
var requestMethod = HttpRequestHeader.GET;
var requestMainHeader = new HttpRequestHeader(requestMethod, requestURI, HttpHeader.HTTP11);

var msg = helper.prepareMessage();
msg.setRequestHeader(requestMainHeader);

helper.sendAndReceive(msg, false);

var redirectedUrl = msg.getResponseHeader().getHeader("Location");
var idToken = redirectedUrl.split("access_token=")[1].split("&")[0];
return { idToken: idToken, nonce: nonce, state: state };
}

function authenticate(helper) {
print("Authenticate function has started executing."); // <-- This is our debug print statement
var sessionToken = getSessionToken(helper);
var authResult = makeAuthorizedRequest(helper, sessionToken);
ScriptVars.setGlobalVar("id_token", authResult.idToken);
ScriptVars.setGlobalVar("nonce", authResult.nonce);
ScriptVars.setGlobalVar("state", authResult.state);
return authResult.idToken;
}

function getRequiredParamsNames(){
return [];
}

function getOptionalParamsNames(){
return [];
}

function getCredentialsParamsNames(){
return [];
}

Simon Bennetts

unread,
Aug 17, 2023, 11:57:17 AM8/17/23
to ZAP User Group
Have you tried using the new ZAP Authentication Auto Detection?

The reason I'm asking is because if you have a modern web app then you will also need to authenticate in the browser.
This can be tricky - using Auto Detection with Browser Authentication will make your life much easier, if it works.

Cheers,

Simon

simon o

unread,
Aug 17, 2023, 12:04:21 PM8/17/23
to ZAP User Group
I haven't tried that until just now. However, the purpose of me doing this is to end up running authenticated scans via the automation framework. It doesn't seem like auto detection exports the entire handshake. 

Would we be able to record a openID handshake via zest or some other easier method? I don't know much about zest scripts and recording in zap.
Message has been deleted

Simon Bennetts

unread,
Aug 18, 2023, 4:09:33 AM8/18/23
to ZAP User Group
Hum, thought I replied to this last night but cant see it now :/
Appologies if you did already get it ;)

Browser based auth doesnt record the entire handshake, it recreates it just as a user would.
This appears to be more effective, particularly when combined with the AJAX spider.
If you just record the handshake then you have problems using the AJAX spider which means you may not be able to explore your app.

If it works in the desktop then it will work in the Automation Framework.

Try it and let us know how you get on.

Cheers,

Simon

simon o

unread,
Aug 21, 2023, 10:13:09 AM8/21/23
to ZAP User Group
So you are saying that it doesn't matter what type of authentication (openID, json, form, etc) and type of session management, this "browser auth" might work? And then it might work for automation framework then later(redo login, refresh session)?

psiinon

unread,
Aug 21, 2023, 10:22:02 AM8/21/23
to zaprox...@googlegroups.com
Thats right :D

Try ZAP Authentication Auto Detection via the Tester Dialog.

If that work then it should also work via the AF. And if it doesnt then we can fix that ;)
If is just fails to find a verification URL then we should still be able to make it work.
And if it fails in another way .. then it all depends on how it fails ;)

Cheers,

Simon

--
You received this message because you are subscribed to the Google Groups "ZAP User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to zaproxy-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/zaproxy-users/39bf5307-ad7b-4be5-bb6e-88bc72b60a6cn%40googlegroups.com.


--
ZAP Project leader

simon o

unread,
Aug 21, 2023, 1:19:37 PM8/21/23
to ZAP User Group
Ok this is really cool i just read through the documentation and tried it out. I used the plugin to do the test - and it worked. However, I am still having issues. Let me give you some background:

- openID used to go from example.com -> okta(302 redirect with access token on URL) -> example.com (takes access_token from url and sets it to storage, so can send Authorization: bearer  header)
- example.com session management is Authorization: Bearer <access_token from okta>
- /api/graphQL is the only endpoint on example.com that requires auth, and needs the authorization: bearer token. However, other pages will render differently based on if you have a authorization bearer header. All the data comes from the graphQL API

Issues:
1. When i attempt to put a verification poll in the authentication test context, it zeros it out when i push test again. So i can't test this well. I am also setting a poll verificaiton every 1 second in my other context to the graphQL endpoint and i don't actually see any of my poll requests in the "history" tab so not sure why that is either.

2. Session management with HTTP headers in zap seems to be where i am failing. Can't quite figure that out. I tried this:
Authorization
Bearer {%url.access_token%}

For some reason this also gets reset. Maybe because i am configuring "Auto-detect authentication"?

3. attempting to run graphQL scan in automation framework to test auth. Once i start job can't figure out how to stop.

Simon Bennetts

unread,
Aug 23, 2023, 6:57:01 AM8/23/23
to ZAP User Group
Replies inline

On Monday, 21 August 2023 at 19:19:37 UTC+2 simono...@gmail.com wrote:
Ok this is really cool i just read through the documentation and tried it out. I used the plugin to do the test - and it worked. However, I am still having issues. Let me give you some background:

- openID used to go from example.com -> okta(302 redirect with access token on URL) -> example.com (takes access_token from url and sets it to storage, so can send Authorization: bearer  header)
- example.com session management is Authorization: Bearer <access_token from okta>
- /api/graphQL is the only endpoint on example.com that requires auth, and needs the authorization: bearer token. However, other pages will render differently based on if you have a authorization bearer header. All the data comes from the graphQL API

Issues:
1. When i attempt to put a verification poll in the authentication test context, it zeros it out when i push test again. So i can't test this well. I am also setting a poll verificaiton every 1 second in my other context to the graphQL endpoint and i don't actually see any of my poll requests in the "history" tab so not sure why that is either.

Yeah, the test is just for ZAP detecting everything.
ZAP will not poll ever second, even if you set the verification to every second. Unless you also try to make an authenticated request :)
If you make no authenticated requests then ZAP will not poll.
If you make authenticated requests then ZAP will poll the verification URL if it can not checked it in the last second (if thats how you have configured it).
 
2. Session management with HTTP headers in zap seems to be where i am failing. Can't quite figure that out. I tried this:
Authorization
Bearer {%url.access_token%}

For some reason this also gets reset. Maybe because i am configuring "Auto-detect authentication"?

I would test this manually via the Automation Framework in the desktop:
  • Use Browser Based auth
  • Auto detect session handling
  • Specify the user creds
  • Specify the verification URL
  • Make one authenticated request via the requester
 

3. attempting to run graphQL scan in automation framework to test auth. Once i start job can't figure out how to stop.

Right now you cannot directly stop AF jobs. You can stop some of the tools directly from their respoctive tabs (in active scan I think).

Also note that GraphQL does not currently support authenticated imports :/
We should really look at that soon :/

Cheers,

Simon

simon o

unread,
Aug 28, 2023, 9:58:53 AM8/28/23
to ZAP User Group
Thank you for the inline replies. 

I have only been testing this, and via the automation framework, in the zap desktop. 

1. What im still stuck on is how to force this "auto-detect authentication" to actually use and send the JWT header for every request. If i could do that, then the GraphQL, and other scan parts would work. 

2. As far as the "session management" goes, are you saying that we shouldn't figure this? Not sure if there is documentation around this with examples that I could look at. Not sure if this is what i am messing up on, or that zap just isn't sending the authorization header because it thinks the page doesn't require authentication. 

psiinon

unread,
Aug 29, 2023, 3:29:42 AM8/29/23
to zaprox...@googlegroups.com
Lets step back so we can understand whats actually going on.
Does the Automation Tester dialog work with your app, ie doe it way that its identified everything required to handle authentication?

Have you tried testing it via the Automation Framework as I suggested in my last reply?

Cheers,

Simon



--
ZAP Project leader

simon o

unread,
Aug 29, 2023, 9:03:22 AM8/29/23
to ZAP User Group
The authentication test does not understand how authentication works for openID/oauth2. Here is how auth works:
1. Go to example.com. Click button "sign in" to be redirected to Okta
2. Sign in with okta (authentication tester can do this)
3. Okta redirects you back to example.com with an "openID" token and "access_token(oauth2)" in the URL
4. Example.com takes those tokens, and will render javascript/html different based on whether you have a valid token or not. 
5. Example.com makes graphQL calls and accepts that openID token from earlier. This returns whether they are authenticated or not, data from database, etc

I have tried testing via automation framework. I tried looking into other ways i could debug or configure auth for OpenID/oauth2. Willing to try anything because many of our apps don't have a signin form they just use openID or SAML instead.

Simon Bennetts

unread,
Aug 29, 2023, 9:12:35 AM8/29/23
to ZAP User Group
How far does the Authentication Tester get?
Is the "sign in" button the problem or is the Okta URL deterministic?

simon o

unread,
Sep 8, 2023, 5:49:34 PM9/8/23
to ZAP User Group
The authentication tester understands how to submit username/password on okta.com. But it doesn't understand how to do that, get access tokens back, and then redirect to example.com.

Okta URL is deterministic in the sense that it is always the same URL. You will get back different tokens every time from OpenID. I guess the "sign in" button on example.com because it just redirects you to okta.com, and doesn't have a form to submit which is what owasp zap typically expects. Does that give you enough information?

Simon Bennetts

unread,
Sep 9, 2023, 4:33:59 AM9/9/23
to ZAP User Group
No where near enough info I'm afraid :)
We need the gory details.
A test site with some test credentials we can use would be ideal. Happy to sign up to a free service if there is a suitable one.
Otherwise we need the information in the Diagnostics tab: https://www.zaproxy.org/blog/2023-05-23-authentication-tester/#what-if-it-doesnt-work and potentially more context (which we will can about after getting the full diags).

Cheers,

Simon
Reply all
Reply to author
Forward
0 new messages