I have self hosted Service Stack server. In client application, it is Windows Form, I want to display login form when user is not authenticated. One way is to try send request and catch WebServiceException like in AuthTest.
I'm looking for #if(Request.IsAuthenticated) (SocialBootstrapApi example) equivalent for client side.
Short answer is there is no way to determine on the client if the User is authenticated or not as the state that determines whether or not a client is Authenticated is stored on the Server.
A user is only Authenticated if there's an Authenticated Session stored in the Users session Id. This SessionId is generally sent in the HTTP Client Cookies which get populated after a successful Authentication attempt, e.g:
var client = JsonServiceClient(BaseUrl);
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true,
});
You can now use the same client (which has its Cookies populated) to make Authenticated Requests to Auth Only Services. Although note if the Server clears out the Users session for any reason the client is no longer authenticated and will throw 401 UnAuthorized HTTP Exceptions at which point they will have to re-Authenticate.
The MVC LiveDemo shows examples of Authentication and retrieving Session on the client with ajax to determine whether the user is authenticated or not by calling /auth, e.g:
$.getJSON("/api/auth", function (r) {
var html = "<h4 class='success'>Authenticated!</h4>"
+ "<table>"
+ $.map(r, function(k, v) {
return "<tr><th>" + v + "<th>"
+ "<td>"
+ (typeof k == 'string' ? k : JSON.stringify(k))
+ "</td></tr>";
}).join('')
+ "</table>";
$("#status").html(html);
}).error(function () {
$("#status").html("<h4 class='error'>Not Authenticated</h4>");
The equivalent with C# is:
try
{
var response = client.Get(new Authenticate());
//Authenticated
}
catch (WebServiceException)
{
//Not Authenticated
}
Related
Trying to get the Single Log out request working with Okta
I've tried using the Sample code on Sustainsys as below:
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return SignOut(new AuthenticationProperties()
{
RedirectUri = "/Index"
},
Saml2Defaults.Scheme);
}
But this doesn't seem to call the Single Log Out. I just get redirected to my Logged Out page. From what I've read it is supported so not sure what I should be calling. In startup I load the metadata which has the SLO url in it so it should know where to be pointing.
Okta requires a cert so I've added the associated pfx file in my startup like so:
// Add Saml Athentication
var authBuilder = services.AddAuthentication();
authBuilder.AddSaml2(options =>
{
// SAML Okta options
options.SPOptions.EntityId = new EntityId(Configuration["SAML:AudienceURI"]);
// Set up redirect to SAML controller callback to handle log in after SAML Authentication (For Okta/IDP initiated log in)
options.SPOptions.ReturnUrl = new Uri(Configuration["SAML:ReturnURL"]);
// Scheme to handle the authentication
options.SignInScheme = IdentityConstants.ExternalScheme;
options.SignOutScheme = IdentityConstants.ApplicationScheme;
// Set up Identity Provider
var identityProvider = new IdentityProvider(
new EntityId(Configuration["SAML:EntityID"]),
options.SPOptions)
{
Binding = Saml2BindingType.HttpRedirect,
LoadMetadata = true,
MetadataLocation = Configuration["SAML:MetadataURL"],
AllowUnsolicitedAuthnResponse = true
};
options.SPOptions.ServiceCertificates.Add(new X509Certificate2("Okta.pfx", ""));
options.IdentityProviders.Add(identityProvider);
});
Any ideas? Do I have to do it manually? If so, any pointers would be great.
Okay must have missed this but the answer is to just redirect to /Saml2/Logout
This still isn't working as the claims needed for logout aren't present but that this step may help someone.
For single logout to work there are a number of requirements that need to be present.
There is a logging message written at https://github.com/Sustainsys/Saml2/blob/74045327e15812c60610746bd0632f1f9236b5bd/Sustainsys.Saml2/WebSSO/LogOutCommand.cs#L175 that will indicate the status of the requirements before deciding whether to do a federated single logout or not.
options.SPOptions.Logger.WriteVerbose("Initiating logout, checking requirements for federated logout"
+ "\n Issuer of LogoutNameIdentifier claim (should be Idp entity id): " + idpEntityId
+ "\n Issuer is a known Idp: " + knownIdp
+ "\n Session index claim (should have a value): " + sessionIndexClaim
+ "\n Idp has SingleLogoutServiceUrl: " + idp?.SingleLogoutServiceUrl?.OriginalString
+ "\n There is a signingCertificate in SPOptions: " + (options.SPOptions.SigningServiceCertificate != null)
+ "\n Idp configured to DisableOutboundLogoutRequests (should be false): " + idp?.DisableOutboundLogoutRequests);
In Asp.Net Core a non-federated logout is a no-op - the Asp.Net core model is that the application needs to both signout the federation scheme and the session authentication scheme.
I am working on an Identity Server implementation that makes use of ASP.net Core 2.1 and IdentityServer4 libraries. In the context of OAuth2 protocol, the identity server is implemented in a way to return an AuthorizationCode as soon as the customer provides his/her login credentials through a server provided web-form. The code is returned by the server to a redirectURI that the customer has provided earlier when he first made the login request (see below shown sample login request).
1) EXAMPLE SCENARIO
Sample Login Request:
http://exampleABC.com:5002/connect/authorize?client_id=XYZ&scope=myscope&response_type=code&redirect_uri=http://exampleXYZ.com
Once above like request is issued in browser, the browser opens up a client login page where user is asked to type in his customerid and password. Then, an SMS token page is opened where the customer enters the SMS he has received at his cell phone. The customer then enters the SMS in the browser. Finally, the server redirects the customer's browser to the page at the redirectURI where the browser shows the AuthorizationCode (i.e. code) in the address bar as shown in the following:
https://exampleXYZ.com/?code=89c0cbe1a2cb27c7cd8025b6cc17f6c7cf9bc0d4583c5a63&scope=myscope
Here, the code "89c0cbe1a2cb27c7cd8025b6cc17f6c7cf9bc0d4583c5a63" can be now used to request an AccessToken from the identity server.
2) PROBLEM STATEMENT
If I re-issue the above indicated sample login request in the same client browser (e.g. chrome), then the browser redirects the user to the redirectURI immediately without re-asking the client login credentials. This is a problem because I have to open up a fresh login screen every time the login request is made considering that there can be customers who have different login credentials. Therefore, I have provided a logout endpoint in my IdentityServer implementation where I intend to clean out the entire client cache and then sign out the customer as shown in the following code block. Here, I delete the cookies first and then create a new one with same key and past expiration date in order that the cookie is removed from the client browser cache in addition to the server cache. My aim here is to bring the login web form up-front in the browser at all times with no caching in place if a logout request is issued in order that the login form is displayed every time a new comer customer arrives.
public async Task<IActionResult> Logout()
{
var vm = await BuildLoggedOutView();
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
try
{
if (HttpContext.Request != null && HttpContext.Request.Cookies != null && HttpContext.Request.Cookies.Keys != null && HttpContext.Request.Cookies.Keys.Count > 0)
{
foreach (var key in _accessor.HttpContext.Request.Cookies.Keys)
{
//!!!! Cookie Removal !!!!!!
//Here I delete the cookie first and then recreate it
//with an expiry date having the day before.
_accessor.HttpContext.Response.Cookies.Delete(key);
_accessor.HttpContext.Response.Cookies.Append(
key,
string.Empty,
new CookieOptions()
{
Expires = DateTime.Now.AddDays(-1)
});
}
}
//!!!! Explicit sign out!!!!!!
await _accessor.HttpContext.SignOutAsync();
}
catch (NotSupportedException ex) // this is for the external providers that don't have signout
{
}
catch (InvalidOperationException ex) // this is for Windows/Negotiate
{
}
return View("Logged out", vm);
}
3) QUESTION:
Although I delete the cookies and override them on server side, the client browser keeps returning into the page at redirect uri where a new authorization code is shown without enforcing the customer to login (which is undesired). So, my question here is what am I missing in the above code block? It looks neither cookie override with old expiry date nor the explicit call to SignoutAsync method does not help to sign out the customer completely. Is there some more explicit strategy you might suggest in order to clean out everything both on client and server side completely once logged out?
I've had the same issue with cookies not being deleted properly. In my case it was because I defined a specific path for the authentication cookies. Let's say my path was /path, in that case you have to specify the same path within your delete:
foreach (var cookie in Request.Cookies.Keys)
{
Response.Cookies.Delete(cookie, new CookieOptions()
{
Path = "/path",
// I also added these options, just to be sure it matched my existing cookies
Expires = DateTimeOffset.Now,
Secure = true,
SameSite = SameSiteMode.None,
HttpOnly = true
});
}
Also, I do not know if the .Append() is necessary. By using .Delete() it already sent a set-cookie header in my case.
The website that I'm working on uses Firebase authentication and different users that login have different permissions as to which pages they can visit.
The way signing in is setup is similar to this post:
User Logins in with two parameters - "id" and "email"
Server uses these to create a custom "uid", then uses the Firebase Admin SDK to create a custom token that is sent back to the client.
The client logs in with the Javascript Firebase SDK - firebase.auth().signInWithCustomToken()
Now that the user is logged in, they can click different pages - i.e. '/foo', '/bar'
The issue I'm running into is that when they visit new pages, I'm trying to pass the token from the client back to the server (almost identical to how its done in this Firebase Doc ), verify the token & check if it has permission to view the webpage.
I'm trying to figure out the best (& most secure) way to do this. I've considered the following option:
Construct a URL with the token, but I've heard this isn't good practice because the token is getting exposed and session hijacking becomes a lot easier.
I've been trying to pass the token in the request header, but from my understanding you can't add headers when the user clicks on a link to a different page (or if its redirected in javascript). The same issue applies to using POST.
What can I do to securely pass this information to the server and check permissions when a user clicks on a link to a different page?
You can get the accessToken (idToken) on client side by:
var accessToken = null;
firebase.auth().currentUser
.getIdToken()
.then(function (token) {
accessToken = token;
});
and pass it in your request headers:
request.headers['Authorization'] = 'Bearer ' + accessToken;
and on your server side get the token with your prefered method and authenticate the request with Firebase Admin SDK, like (Node.js):
firebaseAdmin.auth()
.verifyIdToken(accessToken)
.then(decodedIdToken => {
return firebaseAdmin.auth().getUser(decodedIdToken.uid);
})
.then(user => {
// Do whatever you want with the user.
});
Nowadays, it looks like we're meant to use httpsCallable() client-side to get an object pre-authorized to talk to your endpoint.
eg:
// # ./functions/index.js
exports.yourFunc = functions.https.onCall((data, context) => {
// Checking that the user is authenticated.
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// ... rest of your method
});
// ./src/models/addMessage.js
const firebase = require("firebase");
require("firebase/functions");
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FUNCTIONS PROJECT ID ###'
databaseURL: 'https://### YOUR DATABASE NAME ###.firebaseio.com',
});
var functions = firebase.functions();
// This is the new code:
var yourFunc = firebase.functions().httpsCallable('yourFunc');
yourFunc({foo: bar}).then(function(result) {
// ...
});
From firebase documentation
I am trying to implement the SSO authentication with our WPF application.
Following is scenario
We have our asmx web service hosted with our web application which is firewall protected.
To login our web application or to view our web service in browser from production server we need to login using sso id.
I have to implement the SSO authentication with our WPF application.
First I have to consume the 3rd party web service which return me valid SMSESSION in string format if user is authenticated.
Then I have to pass this SMSESSION to my web service which is behind the fire wall.
Now the problem which I am facing is that I am unable to pass the SMSESSION value to our web service for authentication.
When i Try this it throws an exception
The content type text/html; charset=iso-8859-1 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '<HTML><HEAD><TITLE></TITLE></HEAD><BODY onLoad="document.AUTOSUBMIT.submit();">This page is used to hold your data while you are being authorized for your request.<BR><BR>You will be forwarded to continue the authorization process. If this does not happen automatically, please click the Continue button below.<FORM NAME="AUTOSUBMIT" METHOD="POST" ENCTYPE="application/x-www-form-urlencoded" ACTION="https://xxx.com/ssologinforms/SSO_Generic_RME.fcc?TYPE=33554432&REALMOID=06-49a328ee-0d11-103b-ad01-83323a2d304d&GUID=&SMAUTHREASON=0&METHOD=POST&SMAGENTNAME=-SM-ooM6KmTLLwbCHi%2ffOCEVUabjNhcgULT5joVqmn4M77Tf0PAu3BFcfbexTaiWfZ4N&TARGET=-SM-http%3a%2f%2fdev--srvspeq%2eog%2ege%2ecom%2fSRVSpeQWebService%2easmx"><INPUT TYPE="HIDDEN" NAME="SMPostPreserve" VALUE="+rB+TqoVGXah2Uij3lfKDdCf2jsgE2NAUoujo+dFiiJ7yzqgpQaVnxRangTzcB/faU6BOAAcSBFUMUA1RpOCnVRVjN1cFbL0KhVu3y6IySj4gJE6X797kXkzMNEZgozuEz96g6PgQ6e5+sjqZaaNGii+Cy26QaD16fqQTI5VGkLdnD0fhNvJjXXgSBLNZd77nZz0aaMmz4cGZwnAElk7POTF/NBKdxCXS1l0U/iqqozqfBz0aFqLMpsy'.
please help me out in doing this
I use following code to get the Valid SMSESSION
string userId = txtUsername.Text.Trim(); // User to be authenticate with site minder //xxxxxxxxx
string passwrod = txtPassward.Password.Trim(); //password of user to be authenticate with site minder //xxxxxxxx
string applicationID = "XXXXXX"; // Application id. To be authenticate with JBoss Server.//xxxxxxxx
string applicationPassword = "XXXXXXXXX";//applicationPassword to be authenticate with JBoss Sever.//xxxxxxxx
NamespaceName.AuthenticationServiceClient proxy = new NamespaceName.AuthenticationServiceClient();
AuthenticationServiceRequest request = new AuthenticationServiceRequest();
UserAttributes ut = new UserAttributes();
ut.userName = userId;
ut.password = passwrod;
request.userAttributes = ut;
AuthenticationServiceResponse response = null;
try
{
ServicePointManager.Expect100Continue = false;
proxy.ClientCredentials.UserName.UserName = applicationID;
proxy.ClientCredentials.UserName.Password = applicationPassword;
response = proxy.passwordAuthenticate(request);
if (response.result != null)
{
switch (response.result.ToString())
{
case "SUCCESS":
MessageBox.Show("Valid SMSESSION :" + response.smSession.ToString());
break;
case "ERROR":
MessageBox.Show("ERROR CODE :" + response.errCode.ToString() + "\n Error Message :" + response.errMsg.ToString());
break;
default: MessageBox.Show("Response is neither SUCCESS nor ERROR, its something different\nERROR CODE :" + response.errCode.ToString() + "\n " + response.errMsg.ToString());
break;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
How to pass SMSESSION to asmx web service
I am developing a web site using the following technologies:
MVC 4
EF 5
Web Api
Future - possible Windows Phone/Windows 8 application.
I am using Web API so that I have a developed api that I can use on other clients.
However, I will need to authorise the user each time a request is made to the API. My initial thought was to do this via the HTTP headers. However, I'm just wondering if I should just use MVC Controllers instead of Web API for the MVC application and create a RESTful api if I was to develop a phone/win 8 application, again the user would need to be authenticated. So the originally problem still exists.
What are people's thoughts? Can any one point me to a tutorial on how I could securely pass the authenticated users details over the HTTP Header, also something that's a step by step tutorial as I'm going into this from scratch and need to understand it.
I use basic authentication to pass the credentials for authorization. This puts the credentials in the header. To do this is pretty straight forward by using the beforeSend event handler of the JQuery ajax function. Here is an example of how to do this.
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
This encodes the username/password that is sent in the header. Note that this is not enough security to rely on just the encoding as it is easy to decode. You still want to use HTTPS/SSL to make sure the information sent over the wire is secure.
On the Web API side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authorization process. There is a separate AuthorizeAttribute used by the Web API as opposed to the controller. Be sure to use System.Web.Http.AuthorizeAttribute as your base class when creating your custom AuthorizeAttribute. They have different behaviors. The one for the controller will want to redirect to the logon page whereas the one for the Web API returns an HTTP code indicating success or failure. I return an HTTP code of Forbidden if authorization fails to distinguish a failure due to authorization as opposed to authentication so the client can react accordingly.
Here is an example method for getting the credentials from the header that can be used in the custom AuthorizeAttribute.
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
And here is the code for decoding the header data that is used in this method.
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
Once you have the username and password you can perform your authorization process and return the appropriate HTTP code to the client for handling.
Updated 3/8/2013
I wrote a blog post that goes into more details on how to implement this with SimpleMembership, the default membership provider for MVC 4 Internet Applications. It also includes a downloadable VS 2012 project that implements this.