Unable to use PLAINTEXT signature with a DotNetOpenAuth ServiceProvider - c#

I am building an OAuth 1.0(a) authorization server using DotNetOpenAuth (NuGet package DotNetOpenAuth.OAuth.ServiceProvider, version = 4.1.4.12333). The server is hosted in an ASP.NET application but that's irrelevant to the question.
My ServiceProvider is configured like this:
private ServiceProvider GetServiceProvider()
{
var baseUri = "http://myauth.com";
return new ServiceProvider(
new ServiceProviderDescription
{
UserAuthorizationEndpoint = new MessageReceivingEndpoint(
new Uri(baseUri + "/get_request_token"),
HttpDeliveryMethods.GetRequest
),
RequestTokenEndpoint = new MessageReceivingEndpoint(
new Uri(baseUri + "/authorize"),
HttpDeliveryMethods.PostRequest
),
AccessTokenEndpoint = new MessageReceivingEndpoint(
new Uri(baseUri + "/get_token"),
HttpDeliveryMethods.PostRequest
),
ProtocolVersion = ProtocolVersion.V10a,
TamperProtectionElements = new ITamperProtectionChannelBindingElement[]
{
new PlaintextSigningBindingElement(),
new HmacSha1SigningBindingElement(),
},
},
tokenManager,
new OAuthServiceProviderMessageFactory(tokenManager)
);
}
The relevant part of my get_request_token endpoint looks like this:
var serviceProvider = GetServiceProvider();
var tokenRequest = serviceProvider.ReadTokenRequest();
Now when a consumer sends the following request to this endpoint:
GET /get_request_token?oauth_nonce=C5657420BCE5F3224914304376B5334696B09B7FFC17C105A7F9629A008869DC&oauth_timestamp=1356006599&oauth_consumer_key=sampleconsumer&oauth_signature_method=plaintext&oauth_signature=samplesecret%26&oauth_version=1.0&oauth_callback=http%3a%2f%2flocalhost%3a30103%2fCustomOAuth1 HTTP/1.1
Host: localhost:8180
Connection: close
(broken for clarity):
oauth_nonce=C5657420BCE5F3224914304376B5334696B09B7FFC17C105A7F9629A008869DC
oauth_timestamp=1356006599
oauth_consumer_key=sampleconsumer
oauth_signature_method=plaintext
oauth_signature=samplesecret%26
oauth_version=1.0
oauth_callback=http%3a%2f%2flocalhost%3a30103%2fCustomOAuth1
The serviceProvider.ReadTokenRequest() method throws an exception:
The UnauthorizedTokenRequest message required protections {All} but the channel could only apply {Expiration, ReplayProtection}.
at DotNetOpenAuth.Messaging.Channel.ProcessIncomingMessage(IProtocolMessage message)
at DotNetOpenAuth.Messaging.Channel.ReadFromRequest(HttpRequestBase httpRequest)
at DotNetOpenAuth.Messaging.Channel.TryReadFromRequest[TRequest](HttpRequestBase httpRequest, TRequest& request)
at DotNetOpenAuth.OAuth.ServiceProvider.ReadTokenRequest(HttpRequestBase request)
at DotNetOpenAuth.OAuth.ServiceProvider.ReadTokenRequest()
at OAuthServers.OAuth1.Services.OAuth1Service.Any(GetRequestTokenRequest request)
at lambda_method(Closure , Object , Object )
at ServiceStack.ServiceHost.ServiceRunner`1.Execute(IRequestContext requestContext, Object instance, TRequest request)
On the other hand if the client sends the following request:
GET /get_request_token?oauth_callback=http%3a%2f%2flocalhost%3a65271%2foauth1%2fHandleAccessToken&oauth_consumer_key=sampleconsumer&oauth_nonce=rGFvxlWm&oauth_signature_method=HMAC-SHA1&oauth_signature=HV%2f5Vq%2b0cF3NrtiISE9k4jmgCrY%3d&oauth_version=1.0&oauth_timestamp=1356007830 HTTP/1.1
Host: localhost:8180
Connection: close
(broken for clarity):
oauth_callback=http%3a%2f%2flocalhost%3a65271%2foauth1%2fHandleAccessToken
oauth_consumer_key=sampleconsumer
oauth_nonce=rGFvxlWm
oauth_signature_method=HMAC-SHA1
oauth_signature=HV%2f5Vq%2b0cF3NrtiISE9k4jmgCrY%3d
oauth_version=1.0
oauth_timestamp=1356007830
it succeeds.
As you can see the only difference between those 2 requests is the oauth_signature_method being used. In the first case PLAINTEXT is used whereas in the second HMAC-SHA1.
Is it possible to make DotNetOpenAuth accept a PLAINTEXT signature method for the request token endpoint along with the GET verb (even if the OAuth 1.0(a) specification recommends POST to be used for this endpoint)? Is there some config option that could relax this requirement on the server?
At the moment modifying the client is not an option for me.

OAuth Authentication is done in three steps:
The Consumer obtains an unauthorized Request Token.
The User authorizes the Request Token.
The Consumer exchanges the Request Token for an Access Token.
So here's what that would look like:
public class InMemoryTokenManager : IConsumerTokenManager, IOpenIdOAuthTokenManager
{
private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
public InMemoryTokenManager(string consumerKey, string consumerSecret)
{
if (String.IsNullOrEmpty(consumerKey))
{
throw new ArgumentNullException("consumerKey");
}
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
}
public string ConsumerKey { get; private set; }
public string ConsumerSecret { get; private set; }
#region ITokenManager Members
public string GetConsumerSecret(string consumerKey)
{
if (consumerKey == this.ConsumerKey)
{
return this.ConsumerSecret;
}
else
{
throw new ArgumentException("Unrecognized consumer key.", "consumerKey");
}
}
public string GetTokenSecret(string token)
{
return this.tokensAndSecrets[token];
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
{
this.tokensAndSecrets[response.Token] = response.TokenSecret;
}
public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret)
{
this.tokensAndSecrets.Remove(requestToken);
this.tokensAndSecrets[accessToken] = accessTokenSecret;
}
/// <summary>
/// Classifies a token as a request token or an access token.
/// </summary>
/// <param name="token">The token to classify.</param>
/// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
public TokenType GetTokenType(string token)
{
throw new NotImplementedException();
}
#endregion
#region IOpenIdOAuthTokenManager Members
public void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization)
{
this.tokensAndSecrets[authorization.RequestToken] = string.Empty;
}
#endregion
}

Following block of code may help you to generate plain text signature
public static string GetSignature(OAuthSignatureMethod signatureMethod, AuthSignatureTreatment signatureTreatment, string signatureBase, string consumerSecret, string tokenSecret)
{
if (tokenSecret.IsNullOrBlank())
{
tokenSecret = String.Empty;
}
consumerSecret = UrlEncodeRelaxed(consumerSecret);
tokenSecret = UrlEncodeRelaxed(tokenSecret);
string signature;
switch (signatureMethod)
{
case OAuthSignatureMethod.HmacSha1:
{
var crypto = new HMACSHA1();
var key = "{0}&{1}".FormatWith(consumerSecret, tokenSecret);
crypto.Key = _encoding.GetBytes(key);
signature = signatureBase.HashWith(crypto);
break;
}
case OAuthSignatureMethod.PlainText:
{
signature = "{0}&{1}".FormatWith(consumerSecret, tokenSecret);
break;
}
default:
throw new NotImplementedException("Only HMAC-SHA1 is currently supported.");
}
var result = signatureTreatment == OAuthSignatureTreatment.Escaped
? UrlEncodeRelaxed(signature)
: signature;
return result;
}

Related

WebAPI Token Authorization Bearer Getting Error: The operation was canceled

I am working on .NET Core WebAPI (Core 3.1) project where I have to make call to outside third party API's using OAuth2 Toekn based implementation.
Basically it is 2-Step process. First step is to get Token using Token API Call and then make call to Export API by passing this Token-AccessCode and Input JSON.
I am sucessfully able to get Token using below API Controller Code, but second API call(Export API Method) is throwing error.
Where as when i tested in Postman using Bearer Authorization, it worked fine.
Step(1) In Postman, Authorization tab is set to "No Auth".
Step(2) Header Tab has two settings as below (Authorization and Content-Type).
Step(3) From Body Tab, I am sending below JSON.
{
"oneOf": {
"schedule_date": "08/05/2020",
"request_date": null
},
"start_time": "",
"end_time": "",
"order_number": "",
"status": ""
}
After the above 3 settings, when i call POST method in Postman, it is returning response.
Where as below APICode is throwing error at Line exportClient.PostAsync() call.
Error:
**Exception:**The operation was canceled.
InnerException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
What am i doing wrong?
Appreciate your resposes.
This is the APIController Code:
[HttpGet]
[Route("PullOrders")]
public async Task<IActionResult> PullOrders(
)
{
try
{
var token = await GetElibilityToken();
string exportUrl = "https://XXXXXXX.com/api/v1/export";
var exportClient = new HttpClient();
//exportClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.AccessToken);
exportClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
exportClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var oneData = new OneOf() { schedule_date = "08/05/2020" };
var orderReq = new PullOrderInputRequest() { oneOf = oneData };
var json = JsonConvert.SerializeObject(orderReq, Newtonsoft.Json.Formatting.Indented);
var stringContent = new StringContent(json);
HttpResponseMessage orderResponse = await **exportClient.PostAsync**(exportUrl, stringContent);
}
catch (Exception ex)
{
throw ex;
}
return Ok();
}
private static async Task<Token> GetElibilityToken()
{
var tokenClient = new HttpClient();
string baseAddress = #"https:// XXXXXXX/api/v1/oauth2/token";
string grant_type = "client_credentials";
string client_id = " XXXXXXX";
string client_secret = " XXXXXXXXXXXXXX ";
var form = new Dictionary<string, string>
{
{"grant_type", grant_type},
{"client_id", client_id},
{"client_secret", client_secret},
};
HttpResponseMessage tokenResponse = await tokenClient.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
return tok;
}
internal class Token
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}

Azure Functions OAuth2 from email / password stored in database?

I have a database that contains emails and password hashes.
I would like to secure http trigger's from Azure Functions to allow only authorized call thanks to the Authorization header with a BEARER token.
I think I will need
an http trigger that will generate the token from email/password
Authorize and authenticate the user based on the Authorization header
Can someone get me started on how to create a custom authentication provider or use an existing one and configure Azure Functions to work with it?
Microsoft identity platform supports the OAuth 2.0 Resource Owner Password Credentials (ROPC) grant, which allows an application to sign in the user by directly handling their password.
Get the email(username) and password from database, and send the following request to receive the access token.
POST {tenant}/oauth2/v2.0/token
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=user.read%20openid%20profile%20offline_access
&username=MyUsername#myTenant.com
&password=SuperS3cret
&grant_type=password
You could have look following code snippet, I have tested on azure portal , Azure Function V2:
#r "Newtonsoft.Json"
using Newtonsoft.Json;
using System.Net;
using System.Net.Http.Headers;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
//Parse query parameter
log.LogInformation("C# HTTP trigger function processed a request.");
//Read Request Body
var content = await new StreamReader(req.Body).ReadToEndAsync();
//Extract Request Body and Parse To Class
UserAuthentication objUserInfo = JsonConvert.DeserializeObject<UserAuthentication>(content);
//Message Container
dynamic validationMessage;
//Validate required param
if (string.IsNullOrEmpty(objUserInfo.UserName.Trim()))
{
validationMessage = new OkObjectResult("User name is required!");
return (IActionResult)validationMessage;
}
if (string.IsNullOrEmpty(objUserInfo.Password.Trim()))
{
validationMessage = new OkObjectResult("Password is required!");
return (IActionResult)validationMessage;
}
// Authentication Token Request format
string tokenUrl = $"https://login.microsoftonline.com/common/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "YourApplicationId",
["client_secret"] = "YourApplicationPassword",
["resource"] = "https://graph.microsoft.com",
["username"] = "" + objUserInfo.UserName + "",
["password"] = "" + objUserInfo.Password + ""
});
// Request For Token Endpoint
using (var _client = new HttpClient())
{
var tokenResponse = await _client.SendAsync(tokenRequest);
AccessTokenClass objAccessToken = JsonConvert.DeserializeObject<AccessTokenClass>(await tokenResponse.Content.ReadAsStringAsync());
// When Token Request Null
if (objAccessToken.access_token == null)
{
validationMessage = new OkObjectResult("Invalid Authentication! Please Check Your Credentials And Try Again!");
return (IActionResult)validationMessage;
}
else
{
return new OkObjectResult(objAccessToken.access_token);
}
}
}
catch (Exception ex)
{
validationMessage = new OkObjectResult("Sorry something went wrong! Please check your given information and try again! {0}" + ex.Message);
return (IActionResult)validationMessage;
}
}
Class I have Used:
UserAuthentication Class
public class UserAuthentication
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class AzureFunctionCreateUserClass
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string token_type { get; set; }
public string resource { get; set; }
}
Note: This an sample for azure portal which I have written on azure function . So try to run on there.
Hope this would help.

Validate bearer JWT

I'm quite new to Web API and i still don't get it how to request n JWT.
In My Startup.cs i configured OAuth and my custom JWT:
CustomJwtFormat.cs
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string issuer = string.Empty;
private readonly int timeoutMinutes = 60;
public CustomJwtFormat(string issuer)
{
this.issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audience = "all";
var secret = Convert.FromBase64String("mySecret");
var now = DateTime.UtcNow;
var expires = now.AddMinutes(timeoutMinutes);
string signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
string digestAlgorithm = "http://www.w3.org/2001/04/xmlenc#sha256";
var signingKey = new SigningCredentials(
new InMemorySymmetricSecurityKey(secret), signatureAlgorithm, digestAlgorithm);
var token = new JwtSecurityToken(issuer, audience, data.Identity.Claims,
now, expires, signingKey);
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
return tokenString;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
EDIT: I managed retrieving a token with Postman.
Now my next problem: as long as i send a valid jwt everything works ok. But whenn i send e.g. 'bearer 12345' i get the error message
IDX10708: 'System.IdentityModel.Tokens.JwtSecurityTokenHandler' cannot read this string: '12345'.
...in Visual Studio. Where do i need to put in the token validation? Make a new class and put the TokenSecurityHandler anywhere?

How to add and get Header values in WebApi

I need to create a POST method in WebApi so I can send data from application to WebApi method. I'm not able to get header value.
Here I have added header values in the application:
using (var client = new WebClient())
{
// Set the header so it knows we are sending JSON.
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.Headers.Add("Custom", "sample");
// Make the request
var response = client.UploadString(url, jsonObj);
}
Following the WebApi post method:
public string Postsam([FromBody]object jsonData)
{
HttpRequestMessage re = new HttpRequestMessage();
var headers = re.Headers;
if (headers.Contains("Custom"))
{
string token = headers.GetValues("Custom").First();
}
}
What is the correct method for getting header values?
Thanks.
On the Web API side, simply use Request object instead of creating new HttpRequestMessage
var re = Request;
var headers = re.Headers;
if (headers.Contains("Custom"))
{
string token = headers.GetValues("Custom").First();
}
return null;
Output -
Suppose we have a API Controller
ProductsController : ApiController
There is a Get function which returns some value and expects some input header (for eg. UserName & Password)
[HttpGet]
public IHttpActionResult GetProduct(int id)
{
System.Net.Http.Headers.HttpRequestHeaders headers = this.Request.Headers;
string token = string.Empty;
string pwd = string.Empty;
if (headers.Contains("username"))
{
token = headers.GetValues("username").First();
}
if (headers.Contains("password"))
{
pwd = headers.GetValues("password").First();
}
//code to authenticate and return some thing
if (!Authenticated(token, pwd)
return Unauthorized();
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
Now we can send the request from page using JQuery:
$.ajax({
url: 'api/products/10',
type: 'GET',
headers: { 'username': 'test','password':'123' },
success: function (data) {
alert(data);
},
failure: function (result) {
alert('Error: ' + result);
}
});
Hope this helps someone ...
As someone already pointed out how to do this with .Net Core, if your header contains a "-" or some other character .Net disallows, you can do something like:
public string Test([FromHeader]string host, [FromHeader(Name = "Content-Type")] string contentType)
{
}
Another way using a the TryGetValues method.
public string Postsam([FromBody]object jsonData)
{
IEnumerable<string> headerValues;
if (Request.Headers.TryGetValues("Custom", out headerValues))
{
string token = headerValues.First();
}
}
For .NET Core:
string Token = Request.Headers["Custom"];
Or
var re = Request;
var headers = re.Headers;
string token = string.Empty;
StringValues x = default(StringValues);
if (headers.ContainsKey("Custom"))
{
var m = headers.TryGetValue("Custom", out x);
}
In case someone is using ASP.NET Core for model binding,
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
There's is built in support for retrieving values from the header using the [FromHeader] attribute
public string Test([FromHeader]string Host, [FromHeader]string Content-Type )
{
return $"Host: {Host} Content-Type: {Content-Type}";
}
try these line of codes working in my case:
IEnumerable<string> values = new List<string>();
this.Request.Headers.TryGetValues("Authorization", out values);
For WEB API 2.0:
I had to use Request.Content.Headers instead of Request.Headers
and then i declared an extestion as below
/// <summary>
/// Returns an individual HTTP Header value
/// </summary>
/// <param name="headers"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string GetHeader(this HttpContentHeaders headers, string key, string defaultValue)
{
IEnumerable<string> keys = null;
if (!headers.TryGetValues(key, out keys))
return defaultValue;
return keys.First();
}
And then i invoked it by this way.
var headerValue = Request.Content.Headers.GetHeader("custom-header-key", "default-value");
I hope it might be helpful
app.MapGet("/", ([FromHeader(Name = "User-Agent")] string data) =>
{
return $"User agent header is: {data}";
});
A simple function to get a header value, with a "one-liner" variant using TryGetValue:
private string GetHeaderValue(string key) =>
Request.Headers.TryGetValue(key, out var value)
? value.First()
: null;
var headerValue = GetHeaderValue("Custom");
You need to get the HttpRequestMessage from the current OperationContext. Using OperationContext you can do it like so
OperationContext context = OperationContext.Current;
MessageProperties messageProperties = context.IncomingMessageProperties;
HttpRequestMessageProperty requestProperty = messageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
string customHeaderValue = requestProperty.Headers["Custom"];
For .net Core in GET method, you can do like this:
StringValues value1;
string DeviceId = string.Empty;
if (Request.Headers.TryGetValue("param1", out value1))
{
DeviceId = value1.FirstOrDefault();
}

.net FusiontablesService authentication issue

I am trying to implement google fustion table api in my WPF application to show large number of marker in my google map but problem is this when I am going to authenticate the function "GetAuthorization" never call.
public Fusion()
{
// Create the service.
objService = new FusiontablesService(new BaseClientService.Initializer()
{
Authenticator = CreateAuthenticator()
});
//GetAuthorization(provider);
}
/// <summary>
/// The remote service on which all the requests are executed.
/// </summary>
public FusiontablesService objService { get; private set; }
NativeApplicationClient provider = null;
private IAuthenticator CreateAuthenticator()
{
provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = ClientCredentials.ClientID,
ClientSecret = ClientCredentials.ClientSecret
};
return new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
}
private IAuthorizationState GetAuthorization(NativeApplicationClient client)
{
// You should use a more secure way of storing the key here as
// .NET applications can be disassembled using a reflection tool.
const string STORAGE = "google.samples.dotnet.fusion";
const string KEY = "AIzaSyCtaH=6+";
string scope = FusiontablesService.Scopes.Fusiontables.GetStringValue();
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
CommandLine.WriteError("Using existing refresh token failed: " + ex.Message);
}
}
// Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
Please help me out of this issue.
P.S This code works fine when i am using TaskService and BookService.
Edit : Forgot that the method is not exposed in the IAuthenticator interface
private IAuthenticator CreateAuthenticator()
{
provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = ClientCredentials.ClientID,
ClientSecret = ClientCredentials.ClientSecret
};
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
auth.LoadAccessToken()
return auth;
}

Categories

Resources