User authentication when consuming a REST webservice with ServiceStack - c#

The ServiceStack docs are full of examples on how to use server side implementation of authentication of a user. But how does one set the user credentials on the client side?
I use ServiceStack to consume a JSON REST service like this:
var restClient = new JsonServiceClient (baseUri);
var response = restClient.Get<MyResponse> ("/some/service");
How can I add any form of authentication to the request? The webservice I want to consume uses OAuth 1.0, but I am interested in adding custom authentication, too.
In my code, I have previously performed OAuth token exchange successfully, so I already own a valid access token and need to sign every REST request now using this access token and its token_secret.

ServiceStack's AuthTests shows different ways of authenticating when using the ServiceStack Service Clients. By default BasicAuth and DigestAuth is built into the clients, e.g:
var client = new JsonServiceClient(baseUri) {
UserName = UserName,
Password = Password,
};
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
Behind the scenes ServiceStack will attempt to send the request normally but when the request is rejected and challenged by the Server the clients will automatically retry the same request but this time with the Basic/Digest Auth headers.
To skip the extra hop when you know you're accessing a secure service, you can tell the clients to always send the BasicAuth header with:
client.AlwaysSendBasicAuthHeader = true;
The alternative way to Authenticate is to make an explicit call to the Auth service (this requires CredentialsAuthProvider enabled) e.g:
var authResponse = client.Send<AuthResponse>(new Auth {
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true, //important tell client to retain permanent cookies
});
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
After a successful call to the Auth service the client is Authenticated and if RememberMe is set, the client will retain the Session Cookies added by the Server on subsequent requests which is what enables future requests from that client to be authenticated.

Answering myself, as I've found a nice way to do it using the LocalHttpWebRequestFilter hook in the JsonServiceClient:
For securing a web service with OAuth 1.0a, every http request has to send a special Authorization: header. Within this header field, a hash (signature) must be send that uses some characteristics of the request as input data, like the hostname, request url and others.
Now it seems the LocalHttpWebRequestFilter is called by ServiceStack right before the http request is made, and exposes the underlying HttpWebRequest object, where one can add extra headers and access the required fields of the request.
So my solution is now basically:
var client = new JsonServiceClient (baseUri);
client.LocalHttpWebRequestFilter += (request) => {
// compute signature using request and a previously obtained
// access token
string authorization_header = CalculateSignature (request, access_token);
request.Headers.Add ("Authorization", authorization_header);
};
var response = client.Get<MySecuredResponse> ("/my/service");
Note that I use the Devdefined.OAuth library to do all the heavy stuff in CalculateSignature(). The creation of request token, obtaining user authorization, and exchanging the request token for access token as required by OAuth is done outside of ServiceStack, before the above service calls.

Related

HttpClient send Auth header

I'm using HttpClient to call some REST API with authentication like this:
_networkCredential = new NetworkCredential(username, apiKey);
_httpClientHandler = new HttpClientHandler { Credentials = _networkCredential };
_httpClient = new HttpClient(_httpClientHandler);
This works nicely, but as defined in RFC2617 this will send every request without AUTH header, gets back a 401 and then sends the request a second time with AUTH header.
I'd like to get rid of the extra call without AUTH header to achieve better throughput. For BASIC AUTH I found the following example to manually add the BASIC AUTH header to every call:
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{apiKey}")));
But my service insists on DIGEST AUTH. How can I achieve this in DIGEST AUTH?
I'd guess I'd have to call the service once and cache the AUTH HEADER for subsequent calls. But if the nonce changes I'd need to refresh the AUTH HEADER? How could this be solved?

Azure AD Authentication Breaking HTTP Post Actions When Session Times Out

I recently changed from windows authentication to Azure AD using roughly the "out of the box" code;
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
//AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationContext authContext = new AuthenticationContext(Authority);
return authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
}
}
});
}
Our users have started to get intermittent 404 errors when trying to submit certain forms. I think I have managed to recreate the issue by deleting cookies, so I suspect it's tied to when the session naturally times out.
If I look at the flow with a HTTP GET request it looks like;
HTTP GET https://myappurl/page?param1=value&param2=value
HTTP 302 response with redirect to https://login.microsoftonline.com (including various params; state, client_id etc)
HTTP 200 response (not quite sure how/why it then knows to redirect)
HTTP GET https://myappurl/
HTTP 302 response with redirect to original URL https://myappurl/page?param1=value&param2=value
HTTP GET https://myappurl/page?param1=value&param2=value
HTTP 200 response
Everything works a treat...
For a HTTP POST however;
HTTP POST to https://myappurl/another_page
HTTP 302 response with redirect to https://login.microsoftonline.com (including various params; state, client_id etc)
HTTP 200 response (not quite sure how/why it then knows to redirect)
HTTP GET https://myappurl/
HTTP 302 response with redirect to original URL https://myappurl/another_page
HTTP GET https://myappurl/another_page
HTTP 404 response
Fails because the endpoint only accepts HTTP POST requests.
Any idea if/how I can fix this? I would have thought the built in state tracking or whatever it is doing would store the original request and continue where it left off regardless...
It looks like you are not using the token cache. What this means is that a user's session will expire after about an hour after they sign into the application.
To address this issue you should use AcquireTokenSilentAsync whenever the application needs an access token. This method will automatically refresh the token for you using it's In Memory cache. For more details see https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token

RESTful API call using C#

I am building my URL to make an API call, using the key and secret that the provider has given me.
https://api.testurl.com/api/test/calldata?key=12345&secret=999999&query=hello
My question is I am appending the 'query' based on user input each time and performing the call with the 'key' and 'secret' every time - to me this doesn't seem that secure. Isn't the secret key exposed each time the call is made?
public async Task<List<APIResult.Data>> ApiAsync()
{
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(_apiUrlToCall);
if (!response.IsSuccessStatusCode) return null;
var result = await response.Content.ReadAsStringAsync();
var rootResult = JsonConvert.DeserializeObject<APIResult.Rootobject>
(result);
return rootResult.Data.ToList();
}
}
Normally you'd pass the identity data (in this case your key and secret) in a HTTP header rather than on the querystring. That way it doesn't get logged anywhere (e.g. IIS logs, browser history, slurped by google, facebook et al trackers).
If you're using HTTPS that should stop it being exposed anywhere else.
But yes since HTTP is stateless you have to send some sort of identifying data every time you make a request, be that a secret key, Kerberos token, session coookie, whatever it is.
You can pass the key & secret as Http header. Normally for rest api the Authorization Http Header is set with the authtoken. You could so something similar.

How to pass authorization token to GET method to access authorized resource?

I am new to Token Based authentication. With reference to below links, I am trying to understand Token Based authentication.
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
If the user credentials are valid, I am getting the desired token.
[AcceptVerbs("POST")]
[HttpPost]
public string Post([FromBody]User user)
{
if(user.Username == "hello" && user.Password == "123")
{
var accessTokenResponse = GenerateLocalAccessTokenResponse(user.Username);
return accessTokenResponse.ToString();
}
else
{
return "User invalid";
}
}
Generated token
TWC1Q2rrenZC2p78KPnS4JblcepCg6q3XuxqBQIh7L003npbb6hlBAOYGRN03OvY_O55GWFkZp7UfCmhCgH9Z4rBsjvIrp8gyCp4HmxpP4axVKk10NM9fiG2ctgZWeSbw1jNOor42Wk3yMufbs4xP0RlNuvdpLtBLir52g9rPF053kiJtYryNGzPsbibXHRrNoy0wOR2384uLAJ5pNE9s1DwYpdPKB9uOLSAGhDQOVU,
Now when I try to access the secured resources
[Authorize]
[HttpGet]
// GET api/orders/5
public string Get()
{
return "This is a secure resource";
}
I get "Access Denied Error".
How do I use the token to access such resources.
Any help/suggestion highly appreciated.
Thanks.
usually you would not implement the token endpoint as a POST method in your controller, but create a separate class (SimpleAuthorizationServerProvide) for it as shown in the above mentioned tutorial.
If everything is setup correctly, you have to add an Authorization header to your http request
Authorization: Bearer TWC1Q2rrenZC2p78KP...
and get a reply with status code 200(OK)
To get a token send a request (for example with the tool fiddler) to your token endpoint
e.g. if your service is running on localhost on port 52180 it looks like this:
POST http://localhost:52180/token
grant_type=password&username=admin&password=123&client_id=abc
the grant_type part is the request body.
When you post the above request, you'll reach the token endpoint. Just as Taiseer wrote in Step 12 of the tutorial.
When you put a breakpoint at GrantResourceOwnerCredentials that should be reached as soon as you sent the above request.
The usual flow is:
- client requests a token from http://localhost:52180/token
server authenticates user credentials in GrantResourceOwnerCredentials and issues a token
client reads the access_token from the token response
client adds an authorization header containing the access_token to a request
http://localhost:52180/api/orders
Authorization: Bearer TWC1Q2rrenZC2p78KP...
server reads Authorization header and grants access (if token is valid)
server processes request, eg, the GET request
client receives status 200 and desired data
The api controller shown above looks ok
The [Authorize] attribute is all you need in your controller. That adds an AuthorizationFilter to the http request pipeline, which handles the authorization for you when the client adds the above mentioned Authoriztion header to the request.

How do I do set the Authorization header of a GoogleLogin request using the new WCF REST HttpClient API

I'm using the new HttpClient class, part of the WCF REST Starter Kit, to authenticate to Google's Map Data service. I've got my ClientLogin authentication token, but I'm not sure how to take this instruction:
GET http://maps.google.com/maps/feeds/maps/userID/full
Authorization: GoogleLogin
auth="authorization_token"
and make it work in this code:
var auth = [myAuthToken]
var http = new HttpClient("http://maps.google.com/maps/feeds/maps/[myUserName]/full");
http.DefaultHeaders.Authorization = Microsoft.Http.Headers.Credential.CreateBasic("GoogleLogin", "auth=" + auth);
var response = http.Get();
The docs say: "the GET request requires an Authorization HTTP header, passing an AuthSub or GoogleLogin token." I have the token, I just don't know how to create that Authorization HTTP header correctly through that api. Anyone help?
Instead of using the CreateBasic static method, you can just pass the complete authorization header to the constructor of the Credential class. e.g.
client.DefaultHeaders.Authorization = new Credential("GoogleLogin auth=" + auth);

Categories