We are building a frontend application that is making extensive use of an webapi backend.
In this application we are using oauth to log a user in and store all of the claims that belong to that user inside off access tokens.
On the frontend we are using sentry error logging (http://www.getsentry.com) to log all of our javascript errors. This includes any webapi calls that somehow went wrong or do not return the result we are expecting. With every error we recieve we also log the cookie information along with it, which includes the access tokens that that specific user was using at that time.
Right now we are seeing alot of webapi calls that went wrong because the calls they made are forbidden. I checked some of their access tokens and immeadiately i noticed that the information was way out of date and should have been refreshed a long time ago because the access token should have expired already.
By using :
var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect("tokenhere");
on my localhost machine inside the debugger i can unprotect the access tokens and view the information inside of them.
Like i said the information inside some of these tokens contains invalid information and should have been refreshed already..
Then i noticed that the reason they are not refreshed yet is because the ExpiresUtc is more than 2 years bigger than the IssuedUtc. In other words; The token wont expire until after 2 years.
Here is some example information of one of these tokens
IssuedUtc: { 20/02/2016 03:04:40 +00:00}
ExpiresUtc: {16/11/2018 03:04:40 +00:00}
We are using the default expire time from oauth (20 minutes) and somehow in the pipeline it is setting it to be bigger than 2 years. The only way a user can now refresh their claims is if he logs out and back in again, and obviously until they do so it is creating alot of errors on the frontend side.
By debugging my own tokens i can verify that it is setting it to 20 minutes and have never managed to reproduce it myself.
Solved, we had an controller method hidden somewhere that was issueing access tokens with an expiry date of 2 years.. Doh
Related
We had a working application, we went through the go-live process, and everything was running live for several days. Then we started getting SSL errors, and we saw that the nuget package for the DocuSign package had an update (I believe this was all for the 11/13/2019 2019 certificates auto-update), so we updated our code, but now every request returns the USER_LACKS_MEMBERSHIP error for every token-authenticated request.
Things I can confirm are not the issue:
We have authenticated the app via account.docusign.com and the oauth signature impersonation scope, and the testing and live paths are in the API approved Redirect URIs.
We have the correct base path in the configuration (https://na3.docusign.net, as shown on our Apps and Keys page)
The base path did not change after we get the token back (The BaseUri on the Account object matches what we started with)
We are using the correct user for the configuration (The value labeled "API Username" in the Apps and Keys page is entered as "IMPERSONATED_USER_GUID" in appsettings.json and successfully used in creating the token as parameter UserID, which also matches our user account's ID shown in the backend, so we are not confusing it with TARGET_ACCOUNT_ID or CLIENT_ID, and shuffling those around causes errors much earlier at the token generation step).
We only have one user: the administrator of the DocuSign account. Their ID appears in the API configuration labeled as "API Username". The DocuSign administration backend doesn't display a membership tab anywhere for us to correct any possible issues with a user lacking membership. As far as I can tell, Membership is a higher tier account option than what we're paying for, so I'm confused how we could be having problems with a feature we haven't bought.
We get this error for checking envelope status. We get this error for trying to create new envelopes. We get this error for trying to get Account information. The only thing we can do is get an authentication token, but then that token can't be used to make any further authenticated requests.
Is there anything I'm missing that could be causing this other than some database error on DocuSign that I can't correct through the tools available to me? The package update changed the order of which class constructor accepts the ApiClient object, and there's a new AccessToken field on the Configuration class (which I filled out, but doesn't seem to have any effect, since we're still adding the Authorization/Bearer header manually). I'm out of ideas on what to try next.
Are you using the production environment or the demo environment?
I suspect that what's happening is that you are getting them mixed. As the baseUrl should not be demo.docusign.net etc. if you're using production (as indicated by your na3.docusign.net address) but you must ensure that the same account/user from production is also used.
So, the 4 things to check:
userId
accountId.
baseURI
authURI (account-d.docusign.com vs. account.docusign.com)
All of these should match and be for the same account in the same env.
Using Identity Serve 4 with .Net Core 3.1, razor pages. Also using Cookie Authentication
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
Problem -
In a web application John logged-in 2 times
1st Login on Chrome
2nd Login on edge
So, if John again trying to logged-in on 3rd time on Firefox without logout from previous browsers, then I want to logout John from 1st Login on Chrome forcefully.
I can keep the track of logins in a Session table including Session Id, User Id etc.
But I don’t know how logout user from a particular session using Session Id.
Please help.
Thanks
ASP.NET Core provides an ITicketStore interface which allows you to get control of storing user sessions. Once you provide a class implementing this interface and register it, it will call your class when sessions are being created or verified which you can then store in a database however you like, including attaching arbitrary metadata like browser ID etc.
Now that you have user sessions in your database, you can separately query them and revoke as needed in other logic, including during logins. Since you now provide the session data, simply deleting the record effectively logs the user out from that session. Note that if you use any caching layer to reduce the store requests, you'd need to remove any cached copies as well.
Note that this is separate from IdentityServer and happens with ASP.NET Core itself.
This is a good tutorial that helped me implementing this in my app.
A sample of how it looks to register in Startup, where PersistentTicketStore is my implementation:
// Persistent ticket/cookie store to provide durable user sessions
services.AddSingleton<IUserSessionRepository, UserSessionRepository>();
services.AddSingleton<ITicketStore, PersistentTicketStore>();
services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme)
.Configure<ITicketStore>((options, store) => options.SessionStore = store);
Use the End Session Endpoint
The end session endpoint can be used to end a session and trigger a log out
In the log in process you will need to capture the id_token received from authentication and what user it belongs and store it on some dbo.table. You can use this same table to also keep track if a user has logged in more than once.
To log out a user or end a session you will need to pass the ID you saved as a query string parameter called id_token_hint in a GET call as shown below into:
GET /connect/endsession?id_token_hint={id_token}
For reference see the documentation here https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html#end-session-endpoint
Since you're saying you can keep track of logins, perhaps you should keep track of each session and assign a number somewhere indicating when it was logged in (1 for Chrome, 2 for edge, 3 for Firefox).
Then each time a request is made, you check in your table what the lowest number is (1,2,3 etc), and if the session matches that number, you sign the user out from that session.
await
HttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);
Since each browser will have their own cookie, you can use the above method.
After signing someone out, the next login can be assigned 4, and if 2 makes a request you log that client out.....
Also see this: https://github.com/IdentityServer/IdentityServer4/issues/736
I have implemented this.
When a user logs in, the session id (IUserSession.GetSessionIdAsync) is manually stored in our database. The previous value of this database field is used to create a logout_token which I send to my clients. You can have look at IdentityServer4.Infrastructure.BackChannelLogoutClient to figure out how to create the token and post.
All this assumes you have backchannel logout implemented ofcourse.
I am getting an error when I login using KeyCloak.
The error I get is "Both the access token and the refresh token have expired"
I have followed the example detailed here;
https://github.com/dylanplecki/KeycloakOwinAuthentication/wiki/ASP.NET-MVC-Tutorial
The code works as far as the login page. I enter my login ID and Password, press the Login button and immediately get the above error on a yellow screen.
I have created the client in the Master realm and based all the settings according to what is in the web page above.
Does anyone know what I need to do to fix this?
This was confirmed as a bug:
https://github.com/dylanplecki/KeycloakOwinAuthentication/issues/35#issuecomment-204128345
Are you still having issues?
Here is what you need to do in order to resolve this, and use Keycloak normally:
Since it is reported as a bug, and The Library Author/maintainer is not working on it anymore, you'll have to fix your local copy, and use it instead of the NuGet package.
Background:
Keycloak uses UTC time in all attributes that require time reference, like for example the Token's nbf or exp. Since the Access token is usually valid for 5 minutes, so unless if the machine happens to be in a time zone that matches UTC time, you'll never be able to make the 5 minute limit.
The problem with the library is that it compares the time in the token with DateTime.Now you need to change that manually into DateTime.UtcNow
Implementation:
You can download a copy of the Library Code from GitHub here and browse through the first project in the solution: KeycloakIdentityModel to the file where the task is located: KeycloakIdentity.cs.
Go to line 443, that's the beginning of comparison task GetClaimsAsync.
inside the task, there is an if statement, you'll change all DateTime.Now within into DateTime.UtcNow. the result should look like this:
// Check to update cached claims, but not if refresh token is missing (as in bearer mode)
if ((_kcClaims == null || _accessToken.ValidTo <= DateTime.UtcNow) && _refreshToken != null)
{
// Validate refresh token expiration
if (_refreshToken.ValidTo <= DateTime.UtcNow)
throw new Exception("Both the access token and the refresh token have expired");
// Load new identity from token endpoint via refresh token
await RefreshIdentity(_refreshToken.RawData);
}
changes took place in lines 439 and 442.
after that rebuild the solution, and browse to the physical folders of the solution to ~\src\Owin.Security.Keycloak\bin\Debug, Make sure that the date modified on both of them resembles the actual time and copy both files
KeycloakIdentityModel.dll and Owin.Security.Keycloak.dll to an easy directory to reach (this is not necessary, but recommended for simplicity).
Then from your solution, delete both from the references and add the new ones by right clicking on the reference node in the solution explorer and selecting Add reference. Browse to where you saved them, add them and it should function correctly this way.
I am using the fine-uploader with an asp.net mvc backend to upload files directly to an azure blob-storage. The asp.net-server works as the azure-signature-endpoint(sas). This is all secured with an identityserver3 and here comes the problem: The identity_tokens lifetime is set to 5 minutes(as default). And I want the SAS endpoint to be only called authorized. So a user can only access its own data. But after I have the upload running for the set 5 minutes, it breaks. Of course, because it is not authorized and it returns the login-page from the identity-server(which the fine-uploader, of course, can't handle).
It would be easy to solve this problem, by either setting the lifetime from the token to something higher(but this is unreliable as the upload could take hours or days), or disable the lifetime(yeah would work. But I guess the lifetime has a reason to exist).
So my 2 ideas would be:
to just authorize the user once(when he starts the download)
or to refresh the token by code every time the signature gets generated.
First approach
Can I just set a boolean like user is authorized and if this boolean is set just skip authorization and just return the signature or how could I achieve this
Second approach
How would I do this?
Don't use the id token for API access, an instead use access token. Access tokens are for resources (APIs), whilst id_tokens are purely for authentication. The id_token is only meant to be used to provide the RP with means of knowing who the user is - hence no need for long time span. It's only there for converting into a user session (cookie/principal).
With access tokens you have several approaches for keeping a valid token at a client; either by fetching new ones using the client credentials flow or by using the refresh token approach.
I believe there are samples of both in the samples repo of IdentityServer.
Samples repo link
I am trying to only allow a user account to be logged in, only once at the same time.
E.g. User logs in via the browser on their computer, now they cant login at the same time on their phone as they are already logged in.
I can add a bool property to my user entity, that I can update when the user logs in and logs out.
However, I am using SlidingExpiration on the authentication cookie, therefore when the cookie expires it doesn't update my user property to say they are now logged out.
ExpireTimeSpan = <time period>,
SlidingExpiration = true,
Are there any better approach to restricting concurrent login?
Thanks
Can you generate a Token at log in and store it in Data base?
Then, check every time if the token matches with the one provided by user.
If he does log in in another device, the token will be overwritten and won't match with the first one, so the first session will become invalid.
EDIT:
As you asked in a comment, it doesn't block a user to perform a second log in in another device concurrently, it only invalidates the previous sessions.
Avoiding a second log in requires more job and isn't as safe as the method shown above.
Imagine that the user closes the browser without performing a log out... It will block the session.
An approximation of what you want will be adding the time parameter to your log in (adding it into the data base too, and updating the field on every user's action).
Then show the message of "you can't log in twice" if the token doesn't matches and the time span is not far enough (i.e. 5 minutes). But in my example you need to show a "your session expired" if the token has changed anyway.
"The idea is very simple every time the user logged in you have to generate random token , then you should save that token in the database and in session or if you are using Microsoft form authentication you can save it in the ticket, then each time the user request a page you’ll check if the session token is same as database token , if not kick him out!"
http://engthunder.wordpress.com/2011/10/19/preventing-multiple-user-from-logging-in-using-the-same-username-single-user-login-at-a-time/