I am working on configuring my OAUTH2 server to support the authorization_code flow, but I am hitting a wall trying to exchange an access code for an access_token. Every time I test the call I am getting back an invalid_grant error.
Here is how I understand the flow to work:
The client places a GET request to the oauth server authorize endpoint.
The oauth server validates the client and callback url
The oauth server sends the auth code in a querystring to the callbackurl.
The client (callbackurl) issues a POST request to the token endpoint with a grant_type of authorization_code with the code in the body of the request.
The OAuth server returns the client a token that may be used for requests.
The last method to fire in my code is the AuthenticationTokenProvider.Receive method, but I am getting an invalid_grant error instead of having a token generate. I presume this method should trigger another call someplace, and perhaps I am just missing something in my configuration.
Here is how I have configured OAuth in my environment:
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = allowInsecureHttp,
TokenEndpointPath = new PathString("/oauth2/token"),
AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(HlGlobals.Kernel),
AccessTokenFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel),
AuthorizationCodeProvider = new SimpleAuthenticationTokenProvider()
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
I am able to obtain an authorization code that gets sent to the callback url without issue.
Inside of postman I am using https://www.getpostman.com/oauth2/callback as my callback url. However, I have also tried placing the subsequent request to my token endpoint via a postman POST request as well. Both approaches produce the same result.
The second call, the POST call to the oauth server that includes the code in the body triggers the following events before returning a 400:
OAuthAuthorizationServerProvider - ValidateClientAuthentication
AuthenticationTokenProvider - Receive
In the receive method I verify that the code is valid before calling context.DeserializeTicket(value).
I understand the purpose of the ReceiveCode method to be removing the one time use code from the data store in addition to kicking off the token creation process.... but I am uncertain.
Any thoughts on what I am missing, and how I can implement the server code to exchange an auth code for an auth token using the authorization_code grant_type?
I am happy to supply any additional information upon request. Thanks!
Related
I'm using the below code to get access token and refresh token from docusign.
But I'm always getting the invalid-grant error. I'm pasting the code below.
[HttpGet("GetDocToken")]
[AllowAnonymous]
public async Task<IActionResult> getToken(string docCode)
{
var x = docCode.Length;
var client = new HttpClient();
var authCode=Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("ced8998a-4387-4f30-9ab7-51c0d1af49bf:d7c3ccd4-22fa-4f18-a540-ddf11d8b2c9f"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authCode);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var requestContent = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", docCode),
new KeyValuePair<string, string>("redirect_uri", "http://localhost:4200/auth")
});
HttpResponseMessage response = await client.PostAsync("https://account-d.docusign.com/oauth/token", requestContent);
string resultContent = response.Content.ReadAsStringAsync().Result;
return Ok(response.Content.ReadAsStringAsync());
}
My assumption is that you have received the authentication code back from the DocuSign identity system and are trying to exchange it for an access token.
A couple of issues:
The docs incorrectly indicates that the redirect_uri should be included in the request. The Example Request in the docs does correctly show that the request should only include the grant_type and code parameters.
Note: While the OAuth standard, section 4.1 (d) does indicate that the redirect_url should be included, DocuSign usually does not include it.
My guess is that DocuSign will ignore the redirect_uri parameter, but you might want to try leaving it out.
Another issue is timing: The authorization code you receive back from DocuSign is only good for a minute or so. If you're not immediately using the authorization code (your code's docCode) then you'll get the Invalid Grant error.
Known good example software
I suggest that you also check out the known-good code example for C#. You can use a protocol peeker to see exactly what it is doing during the authentication.
Use a library
I also suggest that you look for an OAuth Authorization Code client library that you can use instead of rolling your own.
For example, are you setting and checking the state value? It's important to do that to stop CSRF attacks. See this article.
Added
It is also not clear to me that you are using the right value as the authorization code.
I believe the flow should be:
User presses "Authenticate with DocuSign" in the Angular app.
User's browser does a GET to the DocuSign authentication server. At this point, the browser is no longer running the Angular app.
User's browser and DocuSign Authentication server exchange HTML back and forth as the user authenticates with DocuSign.
The user completes the authentication process with DocuSign.
DocuSign sends a REDIRECT response to the browser, telling the browser to do a GET on the redirect url. The redirect (and the GET) include query parameters for code and state
Your SERVER (not the Angular app), receives the GET request.
Your server should:
Extract the code and state query parameters
Verify that state is the same as was sent in step 2.
Makes the POST request to DocuSign to exchange the authorization code for an access token.
RESPONDS to the browser with the Angular program.
The Angular program is now once again running on the browser.
Use Implicit Grant
Your other option is to use Implicit Grant. That way you don't need a server component. With Implicit Grant, your Angular program handles the flow.
We currently have a client(MVC .Net Core Web Application) and web API which is used to access Microsoft Graph calls, the authentication process uses Azure AD v2.0 endpoint. we can get this working if we do all of this in the client, however, as soon as we pass the token to the Web API this fails and doesn't even return a response to help us diagnose. below are loads of things we have tried, we have come to the conclusion our azure hasn't been set up correctly but unsure.
Process 1 Works
We forget the web API and do all actions inside the client, this method works but not what we are after so we know the code is not broken.
Process 2 Fails
Client Authenticates to Microsoft graph
redirects with code to the client
send the code to Web API to authenticate, get access token and refresh token
Fails returns nothing
Process 3 Fails
Client Authenticates to Microsoft graph
redirects with code to the client
send the code in the client to authenticate, get access token and refresh token
send access token and refresh token to Web API
use token to perform Microsoft graph action
Fails returns nothing
Azure Settings
Within Microsoft Azure, we have tried configuring the Microsoft Active Directory to follow this example. However, this uses WPF and can't get this to work. We follow it step by step and even the user is prompted with the API permissions be still doesn't work. I am happy to provide any further information to help diagnose this issue.
Our architecture we are trying to achieve.
Update
This is where we exchange the authorization code for an access token however, this gives no return response and times out after about 100 seconds, doesn't even continue.
HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"code", code},
{"client_id", _clientId},
{"client_secret", _clientSecret},
{"redirect_uri", _redirectUri},
{"grant_type", _codeGrantType}
});
using (HttpClient client = new HttpClient())
{
var response = await client.PostAsync(_tokenServerUrl, content);
if (response.IsSuccessStatusCode)
{
//get token from body and update expiry time...
var token = await response.Content.ReadAsJsonAsync<OAuthToken>();
token.ExpiresOn = DateTime.Now.AddSeconds(token.ExpiresIn - 100);
return token;
}
Following the procedures outlined in the SDK docs.
Using .NET's WebRequest to make the API call. StatusCode "Forbidden" being returned along with additional message "Invalid issuer format".
What I'm not sure about is how I'm supposed to generate the token for the call. The instructions in the section I linked to simply say:
REST API calls must be authenticated using a custom HTTP header — X-OPENTOK-AUTH — along with a JSON web token. Create the JWT token with the following claims.
This led me to believe I was to use the JWT library of my choice to create the token. So I did. I used .NET's System.IdentityModel.Tokens.Jwt.
In the .NET section of the site, though, this appears:
You can generate a token either by calling an OpenTokSDK.OpenTok instance's GenerateToken... method, or by calling a OpenTokSDK.Session instance's GenerateToken... method after creating it.
Was that what I was supposed to do? Is that what's wrong?
I can include my code but no point if I've taken the wrong approach altogether.
TokBox Developer Evangelist here.
There are two token concepts within the OpenTok API. One of the tokens is used to authenticate an OpenTok Session and the other is a JWT token used to authenticate each HTTP request you make from the server side.
It looks like the token that you're trying to create is used to interact with the OpenTok REST API. Each request you make to the OpenTok REST API has to have a JWT token which is signed by your API Key and API Secret. The signing would look something like this:
var payload = new Dictionary<string, object>
{
{ "iss", "12321312" }, // apiKey
{ "ist", "project" },
{ "iat", now }, // current time
{ "exp", expiry } // current time + 300 seconds
};
You can use the GenerateJWT method in the OpenTok .NET SDK as reference. Alternatively, you can use the OpenTok .NET SDK to make the StartBroadcast request which would handle the JWT token creation for you.
Lastly, to authenticate an OpenTok Session you have to use one of the OpenTok Server SDKs. Please note that tokens used to authenticate OpenTok Sessions are not created by any library.
I have a mobile client (app) letting the user authenticate with google. So, the client receives an access token and some info (name, email etc) from google. Works fine!
I also created an ASP.NET Web API that the mobile app should comunicate with. On the client side I am adding the token to the HttpClient with:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "pretty_long_access_token_separated_by_two_dots");
Question 1: I'm trying to "decode" the access token on this site (to make sure it's all right): https://jwt.io/
The header and the payload is all right, but it seems like it's an "invalid signature" (says in the bottom). Should I worry about this?
On the server side, I added this to the Configuration method in the Startup class:
app.UseJwtBearerAuthentication( new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new List<string> {"my_client_id"},
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(#"https://accounts.google.com/", "my_client_secret")
},
});
The only thing I want to do with the token, on my server side, is making sure that only validated users from my app should be able to access my API-controller.
Question 2: Is UseJwtBearerAuthentication the right thing for me, or am I going in the wrong direction?
My problem is, I constantly get 401, unauthorized, when trying to access my WEB API controller.
If I am on the right track, I can try to explain more about the server side setup...
Any helt would be very appreciated!
If you are using a JWT token then you will need JWT instead of Bearer
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("JWT", "pretty_long_access_token_separated_by_two_dots");
The signature is used to validate the token as authentic and is therefore only required by the authentication server. If Google is your authentication server, then there should be an API endpoint you can call from your server to verify that the token is valid.
If your server is issuing the JWT token, then you will need to check that the token is valid by decoding the signature using the secret that was used to create it in the first place
If Google is issuing the JWT and you want your server to be able to self validate it, then you need to use another encryption type such as RS256 which will allow you to validate the signature using a public key issued by Google (I have no idea if they provide this method or not)
The reason https://jwt.io/ cannot validate your signature is because it was signed using a secret code. If you have this secret code then you are able to paste it into the textbox in the bottom right. If the secret is correct, and the token hasn't expired, it will show as being a valid JWT token.
I am trying to hook up a website that I am building to FitBit using ASP.NET 5 (rc1-final), Identity and the MS.AspNet.Authentication.OAuth middleware. I am intending to use the Authorization Grant Flow for OAuth 2.0. I have the app set up (details below) on FitBit, and my Startup.cs looks like:
app.UseIdentity();
app.UseOAuthAuthentication(options =>
{
options.AuthenticationScheme = "FitBit-AccessToken";
options.AuthorizationEndpoint = "https://www.fitbit.com/oauth2/authorize";
options.TokenEndpoint = "https://api.fitbit.com/oauth2/token";
options.SaveTokensAsClaims = true;
options.CallbackPath = new PathString("/signing-fitbit-token/");
options.ClientId = "[MY ID STRIPPED OUT]";
options.ClientSecret = "[MY SECRET STRIPPED OUT]";
options.DisplayName = "FitBit";
options.Scope.Add("activity");
options.Scope.Add("heartrate");
options.Scope.Add("location");
options.Scope.Add("nutrition");
options.Scope.Add("profile");
options.Scope.Add("settings");
options.Scope.Add("sleep");
options.Scope.Add("social");
options.Scope.Add("weight");
options.AutomaticAuthenticate = true;
});
When I click the login button, I am directed to the authorization page on FitBit, but when I click Authorize, I am greeted with the ASP.NET dev error page:
An unhandeled exception occurred while processing the request.
HttpRequestException: Response status code does not indicate success: 401 (Unauthorized)
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
I did read here that with some OAuth endpoints (namely Yahoo) they don't like localhost. So, I tried it both with localhost, and modifying my hostfile to a different domain. I have ensured that the redirect url that I am passing in is what is registered for the app at FitBit.
This error is coming from my website, and is getting through to the point where its exchanging the code for the access token. I have fiddler open I'm a bit lost as to where to go from here. I am running on http (since this is local dev and I don't have an ssl cert yet), but I wasn't entirely sure if that mattered.
By default, the OAuth2 generic middleware sends the client credentials by flowing them in the request form (encoded using application/x-www-form-urlencoded).
Sadly, Fitbit only supports basic authentication: since the credentials are not flowed in the Authorization header, Fitbit treats your token request as unauthenticated and rejects it.
Luckily, this is something the dedicated Fitbit social provider (internally based on the OAuth2 generic middleware) will handle for you: https://www.nuget.org/packages/AspNet.Security.OAuth.Fitbit/1.0.0-alpha3
app.UseFitbitAuthentication(options => {
options.ClientId = "230H9";
options.ClientSecret = "ae7ff202cg5z42d85a3041fdc43c9c0b2";
});
Something is going wrong with the OAuth request to FitBit, you need to debug that request and see why you got a 401 back from FitBit.