Solved

Rest API Logout


Userlevel 2
Badge

I have a bunch of .NET “jobs” that sync and pull data from Acumatica for use in an external eCommerce website.

I often hit the login rate limit error and have been told to make sure I logout after each iteration.

 

I call the /identity/connect/token endpoint and pass in my client_id, secret, API Username and API Password with the grant type of password. Its a typical oAuth flow.

 

In testing via postman, I have logged in, did a simple GET query to return some data...then call the logout endpoint (with my bearer token). It returns a 204 success status code.

 

/entity/auth/logout

 

The issue is, I can go back and call the GET query to return some data. I would expect that my bearer token would be invalidated and that GET call would fail. However, it still works. This leads me to believe that I am not truly being logged out.

 

I am reading that I need to pass in a cookie to the logout call, but I can’t really find out how that works. I am not used to working with cookies in a typical oAuth pattern.

 

Can someone point me in the right direction? If I can get this to work...I will add in the Logout method to all my logic and it should hopefully resolve those rate limit errors.

 

Thank you in advance!

icon

Best answer by Dmitrii Naumov 12 April 2023, 02:06

View original

15 replies

Userlevel 7
Badge +5

Hey @eelliston , 

So basically, we need to distinguish two things here:

  1. Token
  2. Session

Your token cannot be invalidated by doing Logout and continues to work the exact same way after the logout. 

What Logout does is it closes a user session. The identification of a session is a cookie. If you call logout without session specified in the cookie, Logout will have no effect.

So, when you use token and make some API call, Acumatica creates a new user session for you (or use existing one if you pass the cookie). 

 

All in all, Acumatica limits the number of open sessions. You can open multiple sessions with the same Token (if you have concurrent_access scope). You should take care of those sessions and close them properly. (or you can remove the concurrent_access and always work with the single session)

Userlevel 1

I’m running into this issues as well. Logout does not remove my API user from the Active Users list.

const logout_data = await fetch(`${acumatica_host_url}${acumatica_logout_url}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Set-Cookie': `ASP.NET_SessionId=${asp_session_id}`
}
});

asp_session_id contains the value of ASP.NET_SessionId that was returned during my initial login call. What I find interesting is that if you go to the Active Users table then check the source code, you’ll see that each user has a hidden session field. However, this does not match the initial session I was given during login. Either way though, when I pass either of them to the call above, my user still remains logged in.

Userlevel 1

I used WireShark for some debugging looks like the culprit was using “Set-Cookie” instead of just “cookie”. 

Userlevel 1

One more thing, the documentation does not mention it, but you must pass the bearer token back too, not just the ASP.NET_SessionId cookie.

Slightly similar for direct style login as well, but you must include the “.ASPXAUTH” (the dot prefix is not a mistake) cookie in addition to the ASP.NET_SessionId cookie, which the docs do not mention.

I just ran into something similar with HTTPS/OAuth. . . 

I had to hand in the session id as a cookie instead of set-cookie - probably more my lack of experience than any fault with Acumatica or the documentation here but, worth noting. That said, the lack of error message/response on the logout makes it super frustrating to work with. . . 

Userlevel 1

I just ran into something similar with HTTPS/OAuth. . . 

I had to hand in the session id as a cookie instead of set-cookie - probably more my lack of experience than any fault with Acumatica or the documentation here but, worth noting. That said, the lack of error message/response on the logout makes it super frustrating to work with. . . 

The lack of error messages is wild in Acumatica. I personally volunteer to go in and add the appropriate error message. Like how does system tell you that one of the fields you sent over is not apart of the object...but can’t tell you which one? I’ll just blame it on C# since that’s a favorite pastime of mine :D

Do you happen to know if one needs to hand in the session id when getting a new access token?

 

I’ve been struggling to get Acumatica to return a refresh token so I need to be able to retrieve a new access token once it expires without triggering a new session/login. 

When I specify the scope api+offline_access I get an invalid scope error rather than a refresh token. . . not sure what I’m doing wrong here considering I’m following the documentation - at least as I understand it.

Userlevel 1

I’ll just post my workflow because you should be getting a json object that looks like this

{
access_token: string,
expires_in: number,
token_type: string,
refresh_token: string,
scope: string
}

 

Initial login

const form = new FormData();
form.set('grant_type', 'password');
form.set('client_id', SECRET_ACUMATICA_CLIENT_ID);
form.set('client_secret', SECRET_ACUMATICA_SHARED_SECRET);
form.set('username', SECRET_ACUMATICA_USERNAME);
form.set('password', SECRET_ACUMATICA_PASSWORD);
form.set('scope', 'api offline_access');

const auth_data = await fetch(`${acumatica_host_url}${acumatica_auth_url}`, {
method: 'POST',
body: form
});

const cookies = auth_data.headers.get('set-cookie') ?? '';

const session_id = getCookie('ASP.NET_SessionId', cookies);
const token_data = await auth_data.json();

//token_data contains this
/**
{
access_token: string,
expires_in: number,
token_type: string,
refresh_token: string,
scope: string
}
*/
....
//Custom function I use to extract a cookie
function getCookie(name, cookies) {
var re = new RegExp(name + '=([^;]+)');
var value = re.exec(cookies);
return value != null ? value[1] : '';
}

 

Refresh using the token_data I received above

const refresh_token = token_data['refresh_token'];

const form = new FormData();
form.set('grant_type', 'refresh_token');
form.set('client_id', SECRET_ACUMATICA_CLIENT_ID);
form.set('client_secret', SECRET_ACUMATICA_SHARED_SECRET);
form.set('refresh_token', refresh_token);

const auth_data = await fetch(`${acumatica_host_url}${acumatica_auth_url}`, {
method: 'POST',
body: form
});

const token_data = await auth_data.json();
//token_data contains this again
/**
{
access_token: string,
expires_in: number,
token_type: string,
refresh_token: string,
scope: string
}
*/

 

Logout

const access_token = token_data['access_token'];

await fetch(`${acumatica_host_url}${acumatica_logout_url}`, {
method: 'POST',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${access_token}`,
'Content-Type': 'application/json',
Cookie: `ASP.NET_SessionId=${session_id}`
}
});

 

That helps a lot! Thanks!

 

I was handing the scopes through incorrectly - I’m receiving the refresh token now. Thanks!

Userlevel 2
Badge

If you call logout without session specified in the cookie, Logout will have no effect.

 

My testing shows that this is not exactly true.

If I execute some “GET” API call with correct Token, but without session cookie, then user session will be opened. Then if I execute a logout call and pass the same Token in the “Authorization” header and do not pass session cookie, then logout call will anyway close the session successfully.

Best regards,

Andrey

Userlevel 1

If you call logout without session specified in the cookie, Logout will have no effect.

 

My testing shows that this is not exactly true.

If I execute some “GET” API call with correct Token, but without session cookie, then user session will be opened. Then if I execute a logout call and pass the same Token in the “Authorization” header and do not pass session cookie, then logout call will anyway close the session successfully.

Best regards,

Andrey

Are you executing this from code or an app like Postman or Thunder Client? I noticed this in my initial attempts and it turned out Thunder Client was sending the session id behind the scenes.

Userlevel 2
Badge

...

Are you executing this from code or an app like Postman or Thunder Client? I noticed this in my initial attempts and it turned out Thunder Client was sending the session id behind the scenes.

 

I’m executing this in SQL (see below code snippet), with this I am controlling what is passed into both headers “Cookie” and “Authorization”.
Also, I am recording returned headers using exec sp_OAGetProperty @GetHttpRequest, 'getAllResponseHeaders', and I’m not seeing ASP.NET_SessionId returned back from my calls.

EXEC sp_OACreate 'MSXML2.ServerXMLHTTP', @GetHttpRequest OUT
EXEC sp_OAMethod @GetHttpRequest, 'open', NULL, 'GET', @GetUrl, false
EXEC sp_OAMethod @GetHttpRequest, 'setRequestHeader', NULL, 'Cookie', @Cookies
EXEC sp_OAMethod @GetHttpRequest, 'setRequestHeader', NULL, 'Content-Type', 'application/json'
EXEC sp_OAMethod @GetHttpRequest, 'setRequestHeader', NULL, 'Connection', 'keep-alive'
EXEC sp_OAMethod @GetHttpRequest, 'setRequestHeader', NULL, 'Authorization', @AccessToken
EXEC sp_OAMethod @GetHttpRequest, 'send', NULL, NULL

 

Userlevel 6
Badge +6

Hey @eelliston , 

So basically, we need to distinguish two things here:

  1. Token
  2. Session

Your token cannot be invalidated by doing Logout and continues to work the exact same way after the logout. 

What Logout does is it closes a user session. The identification of a session is a cookie. If you call logout without session specified in the cookie, Logout will have no effect.

So, when you use token and make some API call, Acumatica creates a new user session for you (or use existing one if you pass the cookie). 

 

All in all, Acumatica limits the number of open sessions. You can open multiple sessions with the same Token (if you have concurrent_access scope). You should take care of those sessions and close them properly. (or you can remove the concurrent_access and always work with the single session)

@Dmitrii Naumov - Thanks for this!  Can you provide an example for this?  Our dev team and others are having an issue calling the token to properly end the session. 
 

Thanks!

 

 

Userlevel 7
Badge +5

If you call logout without session specified in the cookie, Logout will have no effect.

 

My testing shows that this is not exactly true.

If I execute some “GET” API call with correct Token, but without session cookie, then user session will be opened. Then if I execute a logout call and pass the same Token in the “Authorization” header and do not pass session cookie, then logout call will anyway close the session successfully.

Best regards,

Andrey

@abaranovhs that depends on the ‘Concurrent Access’ API scope. If the concurrent access is not present, the token is actually enough to identify the session (since only one session is possible in that case).

 

@jamesh if you don’t need to have parallel open sessions, I recommend to just not request  the ‘Concurrent Access’ API scope. If you do need to open multiple sessions, you do need to manage that. 

E.g. if you make a new API call without Session ID defined in cookies the new session ID is opened and returned to you in the cookie. You need to log it out once you are done with that. 

Hope that helps.

 

 

 

Userlevel 1

Hey @eelliston , 

So basically, we need to distinguish two things here:

  1. Token
  2. Session

Your token cannot be invalidated by doing Logout and continues to work the exact same way after the logout. 

What Logout does is it closes a user session. The identification of a session is a cookie. If you call logout without session specified in the cookie, Logout will have no effect.

So, when you use token and make some API call, Acumatica creates a new user session for you (or use existing one if you pass the cookie). 

 

All in all, Acumatica limits the number of open sessions. You can open multiple sessions with the same Token (if you have concurrent_access scope). You should take care of those sessions and close them properly. (or you can remove the concurrent_access and always work with the single session)

@Dmitrii Naumov - Thanks for this!  Can you provide an example for this?  Our dev team and others are having an issue calling the token to properly end the session. 
 

Thanks!

 

 

Does my “Logout” example above help at all?

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved