I've set up an STS with WIF and want to expose whether a user is signed in so that an RP can determine if the user is signed in without requiring the user to redirect to the STS and return. If the user is signed in, a different process flow will occur on the RP so it's important to know but not to force the sign-in at this point in the process.
My plan was to create a simple generic handler on the STS which, when hit via an HttpWebRequest, returns the output of context.User.Identity.IsAuthenticated (where context is the HttpContext passed into the ProcessRequest method:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
if (context.User != null && context.User.Identity.IsAuthenticated)
context.Response.Write("True");
else
context.Response.Write("False");
}
My problem is, if the user is signed in and I hit this handler directly, it returns True but if I programmatically hit the handler from the RP, it returns False (in fact the Identity is null).
Am I just completely wrong in doing this and hitting the handler from the RP will return the status of the User on the RP or could I be doing something wrong?
This handler will only work (return true), if you send the STS authentication cookies with the request. Only your web browser may have these cookies. Therefore it can't be done via HttpWebRequest. Also this is why it works, when you call the handler directly from the browser.
I know this is a bit old thread, but the answer may help others who land on this page.
The thing that does the magic behind the authentication is the session and authentication cookies which are sent to the user's client (e.g. browser) from your STS app. I'm not sure how your STS and RP apps are designed and communicate, so I will keep the answer generic. To notify your RP app of the authentication status, you need to:
1) either somehow share both cookies between the user's client and the your RP app. In this scenario, I'm afraid you will have to build your own client and make your users use it to visit the STS app. This is because you cannot get the cookies from the standard browsers. The client you build sends the cookies somewhere where your RP app can get them and place them in HttpWebRequest.CookieContainer which then can successfully get the result of your handler. I'm only explaining this method to say that it is doable and show how complex and twisted it is.
2) or you will have to track the login status of your users. Instead of checking the context.User, your handler must get the user ID from the calling RP app and then check if that user is logged in (that is there is an active session for that user). For example, you can track or store your sessions in the database, or have a look at the following thread for some methods of accessing active sessions:
List all active ASP.NET Sessions
Related
I am using Visual Studio 2015 Enterprise and ASP.NET vNext Beta8 to issue and consume JWT tokens as described here.
In our implementation we're storing some client details in Redis at token issuing time and we would like the flush this information when the user logs out.
My question is what is the best practices for logging out with OIDC?
While I could roll my own contoller for this purpose I couldn't help but notice Open ID Connect (OIDC) seems somewhat primed to handle this case. For example OIDC has an OnLogoutEndpoint handler and LogoutEndpointPath settings. But when I call the OIDC logout URI that handler appears to accept any random x-www-form-urlencoded form I throw at it and doesn't in any particular way seem to be demanding the presence of a token.
Any advice on proper OIDC logout practices would be very much appreciated.
In AspNet.Security.OpenIdConnect.Server, the logic used for the logout endpoint is left as an exercise.
In this sample, it is implemented using an MVC 6 controller, where you're - of course - free to add custom logic to remove cached details from your Redis server.
[HttpPost("~/connect/logout")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout() {
// When invoked, the logout endpoint might receive an unauthenticated request if the server cookie has expired.
// When the client application sends an id_token_hint parameter, the corresponding identity can be retrieved using AuthenticateAsync.
var identity = await HttpContext.Authentication.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme);
// Remove the cached details here. If you need to determine
// who's the authenticated user, you can use the identity variable.
// Remove the authentication cookie and return the user to the client application.
return SignOut("ServerCookie", OpenIdConnectServerDefaults.AuthenticationScheme);
}
You can also do something similar directly from the LogoutEndpoint event. Don't forget to call context.HandleResponse() to make sure the request is not intercepted by another middleware.
I'm using Google Sign-In. A user comes to my site and logs in with gapi.auth2.getAuthInstance().signIn(), or they are already logged in and when the page loads (or reloads) we fetch the status. At this point I have an identity token good for an hour that I can validate on the server.
When a user leaves the browser sitting (say, overnight), this token expires. gapi.auth2.getAuthInstance().isSignedIn.get() returns true, but the token does not validate.
How can I log in a user and keep them logged in while their session is active (ie, browser hasn't been closed)? Or refresh the token? Anything more graceful than reloading the page...
Edit: The refresh token is not a correct answer; I don't want offline access (and don't want to ask for the permission). Google obviously thinks the user is still signed into my application; the user can reload the page and get a new token without providing credentials again. Surely there is some mechanism more graceful than a hidden iframe to get an updated token?
If the token is expired, you can call gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse(). It returns a Promise.
I've raised an issue with Google over this because it's simply ridiculous they haven't documented this properly.
My comment here advises how I've accomplished refresh using the above.
FWIW, we've managed to (mostly) make it work via a listener approach. It appears that 'userChanged' callback is invoked ~5 minutes before the access token expires. That's enough for us to extract and update the access token without refreshing the page.
What does not quite work though is when computer comes back from sleep. This can be solved relatively easy by reloading the page on wake up.
You can accomplish this with listeners.
var auth2 = gapi.auth2.getAuthInstance();
// Listen for changes to current user.
// (called shortly before expiration)
auth2.currentUser.listen(function(user){
// use new user in your OpenID Connect flow
});
This will allow you to keep current credentials, as long as the browser remains active.
If the computer is put to sleep additional work must done to get current credentials.
if (auth2.isSignedIn.get() == true) {
auth2.signIn();
}
You can use Refresh Token to get offline access. As per the official reference
Access tokens have limited lifetimes. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens.
Basically you will get the refresh token the first time you ask for authentication. You need to save that token securely for future use. The access token (you mentioned as identity token) expires after an hour. After that you have to use the refresh token each time you want to get a new usable access token.
Depending on the client library you are using the syntax will differ. following is a sample for php client library.
// get access token from refresh token if access token expire
if($client->getAuth()->isAccessTokenExpired()) {
$client->refreshToken($securelyPreservedRefreshToken);
$newToken = $client->getAccessToken();
}
check this for details steps.
Is there a way to pass a value back to a relying party after login? e.g. on the querystring?
Background:
What we want to do is inform the relying party what action the user took, e.g. sign in or register, so that the relying party can display the appropriate confirmation message to the user. Because the relying party might link to a Sign Up page, but then instead of signing up the user signs in, so the relying party shouldn't display a "thanks for joining us" notification panel.
I tried adding &lastaction=signup to the returnUrl but that gets lost when the form is posted through Azure ACS.
Next attempt was to try to add lastaction to the wreply, like so:
WSFederationMessage message;
WSFederationMessage.TryCreateFromUri(uri, out message);
var signinMessage = wsFederationMessage as SignInRequestMessage;
if (signinMessage != null)
{
signinMessage.Reply += "?lastaction=hello";
...
In Fiddler I can see that the next POST to ACS posts to https://xxxxx.accesscontrol.windows.net/v2/wsfederation?lastaction=hello
But the lastaction is not passed on to my relying party.
We had a related problem: we wanted to let the RP know which authentication methods the user used when signing in. We solved this by creating a new "system" claim with our namespace, and put the information in there.
In our TokenService implementation, in the AddSecurityClaims method:
claimsIdentity.AddClaim(
new Claim(
String.Format("{0}/{1}", WellKnownConfiguration.TokenService.ClaimsNamespace,
ClaimsAuthenticationMethods), ((int) userAuthenticationMethods)));
Update
You mentioned you thought about using cookies. In that case, I would do the following. I would implement setting a cookie (e.g. when registration page) and then create one more "action" that would return the value of that cookie. When the app gets the POST request with the credentials, you'd perform a redirect (immediately) to that relaying action with a return url. That action would then append the value of the cookie and call the original RP, but a custom action, that would then properly display the view.
Think of it as a cookie proxy. To summarize, the process is as follows:
User hits the RP, action requires authentication
The RP redirects the user to the STS as per WS-Federation
STS issues a token, and also adds a cookie to its own domain
RP gets the authenticated user, redirects to STS Cookie Reader
STS redirects to RP's second screen that can handle the login properly
All in all, one more hop, but like I said, it's probably fast enough for the user to not notice and/or care.
I have this scenario.
RP with passive federation to 2.
Custom STS for user/password authentication
Everything is working fine. So far the user would press login link, which would go to a restricted area, thus the federation security was triggered, and login screen appeared. It would prompt him to write the credentials, the request was then processed, etc.
Now I'm required to create login (user/password) text-boxes on the same page (default page). How can I achieve federation sign-in operation without redirecting to a login page? Should (or can) I use FederatedPassiveSignIn control? If so, how?
You could show the login boxes on the unprotected landing page if IsAutheticated is false and then send a message to the custom STS login page with the credentials encrypted or whatever which then logs in behind the scenes and redirects back to your app. with the token in the normal manner.
However, if the user is not authenticated and bookmarks a page behind the landing page, they'll be redirected to the STS.
For anyone interested (I doubt someone actually is), I've solved it through - basically - simulating what login page does.
// makes credentials validation, and creates IClaimsPrincipal with the acquired claims
IClaimsPrincipal principal = LoginHelper.SignIn(editEmail.Value, editPassword.Value);
// retrieves the instance of the STS (in this case my custom STS)
TrustedSecurityTokenService service = (TrustedSecurityTokenService) TrustedSecurityTokenServiceConfiguration.Current.CreateSecurityTokenService();
// creates the request manually to point to my original page (or whatever page you desire)
SignInRequestMessage request = FederatedAuthentication.WSFederationAuthenticationModule.CreateSignInRequest(Guid.NewGuid().ToString(), "http://page/i/want/to/go/after/the/validation.aspx", true);
// processes first the request...
SignInResponseMessage response = FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(request, principal, service);
// ...then the response is processed, and redirected to URL above
FederatedPassiveSecurityTokenServiceOperations.ProcessSignInResponse(response, Response);
This created cookies, and principal is not IsAuthenticated. As if it were process by login page (at least it seems to work so far as expected).
I want to know more about win32 LogonUser api function. The last parameter is a token which can be used to impersonate a windows identity to execute code on a person's behalf. Say I have a login page where I enter my username, password and domain. When the user submits the page I validate the user by making a call to LogonUser() and get a token reference.
I am thinking why not store the token in a cookie and use it at a later stage (perhaps in another page). I just don't know what issues I might have to face upfront...
Can the token expire even if we don't close it properly using the CloseHandle() win32 call? Is there any article related with this particular requirement?