AAD authentication getting access token - authentication

I am trying to get an access token for powerbi users, following this link - https://learn.microsoft.com/en-us/power-bi/developer/automation/walkthrough-push-data-get-token
But I am getting an error at this line of code
var token = authContext.AcquireTokenAsync(resourceUri, clientID, new Uri(redirectUri)).Result.AccessToken;
Error:
clinetID is - cannot convert from 'string' to 'microsoft.identitymodel.clients.activedirectory.clientcredential'
new Uri(redirectUri) = cannot convert from 'System.uri' to 'microsoft.identitymodel.clients.activedirectory.User'
Code for getting access token:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Threading.Tasks;
namespace Service
{
public class PowerbiService
{
private static string token = string.Empty;
static void Main(string[] args)
{
//Get an authentication access token
token = GetToken();
}
#region Get an authentication access token
private static async Task<string> GetToken()
{
string clientID = "{Client_ID}";
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
string resourceUri = "https://analysis.windows.net/powerbi/api";
string authorityUri = "https://login.microsoftonline.net/common/";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
var token = authContext.AcquireTokenAsync(resourceUri, clientID, new Uri(redirectUri)).Result.AccessToken;
Console.WriteLine(token);
Console.ReadLine();
return token;
}
#endregion
}
}

Related

Azure Devops Oauth authentication: Cannot get access token (BadRequest Failed to deserialize the JsonWebToken object)

I'm trying to implement an OAUth 2.0 flow for custom webapplication for Azure Devops. I'm following this https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops documentation as well as this https://github.com/microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample OauthWebSample but using ASP.NET Core (I also read one issue on SO that looked similar but is not: Access Azure DevOps REST API with oAuth)
Reproduction
I have registered an azdo app at https://app.vsaex.visualstudio.com/app/register and the authorize step seems to work fine, i.e. the user can authorize the app and the redirect to my app returns something that looks like a valid jwt token:
header: {
"typ": "JWT",
"alg": "RS256",
"x5t": "oOvcz5M_7p-HjIKlFXz93u_V0Zo"
}
payload: {
"aui": "b3426a71-1c05-497c-ab76-259161dbcb9e",
"nameid": "7e8ce1ba-1e70-4c21-9b51-35f91deb6d14",
"scp": "vso.identity vso.work_write vso.authorization_grant",
"iss": "app.vstoken.visualstudio.com",
"aud": "app.vstoken.visualstudio.com",
"nbf": 1587294992,
"exp": 1587295892
}
The next step is to get an access token which fails with a BadReqest: invalid_client, Failed to deserialize the JsonWebToken object.
Here is the full example:
public class Config
{
public string ClientId { get; set; } = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
public string Secret { get; set; } = "....";
public string Scope { get; set; } = "vso.identity vso.work_write";
public string RedirectUri { get; set; } = "https://....ngrok.io/azdoaccount/callback";
}
/// <summary>
/// Create azdo application at https://app.vsaex.visualstudio.com/
/// Use configured values in above 'Config' (using ngrok to have a public url that proxies to localhost)
/// navigating to localhost:5001/azdoaccount/signin
/// => redirect to https://app.vssps.visualstudio.com/oauth2/authorize and let user authorize (seems to work)
/// => redirect back to localhost:5001/azdoaccount/callback with auth code
/// => post to https://app.vssps.visualstudio.com/oauth2/token => BadReqest: invalid_client, Failed to deserialize the JsonWebToken object
/// </summary>
[Route("[controller]/[action]")]
public class AzdoAccountController : Controller
{
private readonly Config config = new Config();
[HttpGet]
public ActionResult SignIn()
{
Guid state = Guid.NewGuid();
UriBuilder uriBuilder = new UriBuilder("https://app.vssps.visualstudio.com/oauth2/authorize");
NameValueCollection queryParams = HttpUtility.ParseQueryString(uriBuilder.Query ?? string.Empty);
queryParams["client_id"] = config.ClientId;
queryParams["response_type"] = "Assertion";
queryParams["state"] = state.ToString();
queryParams["scope"] = config.Scope;
queryParams["redirect_uri"] = config.RedirectUri;
uriBuilder.Query = queryParams.ToString();
return Redirect(uriBuilder.ToString());
}
[HttpGet]
public async Task<ActionResult> Callback(string code, Guid state)
{
string token = await GetAccessToken(code, state);
return Ok();
}
public async Task<string> GetAccessToken(string code, Guid state)
{
Dictionary<string, string> form = new Dictionary<string, string>()
{
{ "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
{ "client_assertion", config.Secret },
{ "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
{ "assertion", code },
{ "redirect_uri", config.RedirectUri }
};
HttpClient httpClient = new HttpClient();
HttpResponseMessage responseMessage = await httpClient.PostAsync(
"https://app.vssps.visualstudio.com/oauth2/token",
new FormUrlEncodedContent(form)
);
if (responseMessage.IsSuccessStatusCode) // is always false for me
{
string body = await responseMessage.Content.ReadAsStringAsync();
// TODO parse body and return access token
return "";
}
else
{
// Bad Request ({"Error":"invalid_client","ErrorDescription":"Failed to deserialize the JsonWebToken object."})
string content = await responseMessage.Content.ReadAsStringAsync();
throw new Exception($"{responseMessage.ReasonPhrase} {(string.IsNullOrEmpty(content) ? "" : $"({content})")}");
}
}
}
When asking for access tokens the Client Secret and not the App Secret must be provided for the client_assertion parameter:

Why [Authorize] attribute return 401 status code JWT + Asp.net Web Api?

I'm having big trouble finding issue with the JWT token authentication with asp.net web api. This is first time I am dealing with JWT & Web Api authentication & Authorization.
I have implemented the following code.
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new OAuthTokenProvider(),
RefreshTokenProvider = new RefreshTokenProvider(),
AccessTokenFormat = new Provider.JwtFormat("http://localhost:49860")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = "http://localhost:49860";
string audienceId = Config.AudienceId;
byte[] audienceSecret = TextEncodings.Base64Url.Decode(Config.AudienceSecret);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
OAuthTokenProvider.cs
public class OAuthTokenProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// validate client credentials (demo)
// should be stored securely (salted, hashed, iterated)
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
/***Note: Add User validation business logic here**/
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{ "as:client_id", "Kaushik Thanki" }
});
ClaimsIdentity oAuthIdentity = new ClaimsIdentity("JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, props);
context.Validated(ticket);
}
}
JwtFormat.cs
public class JwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
public JwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = Config.AudienceId;
string symmetricKeyAsBase64 = Config.AudienceSecret;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
RefreshTokenProvider.cs
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
// maybe only create a handle the first time, then re-use for same client
// copy properties and set the desired lifetime of refresh token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
};
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
//_refreshTokens.TryAdd(guid, context.Ticket);
_refreshTokens.TryAdd(guid, refreshTokenTicket);
// consider storing only the hash of the handle
context.SetToken(guid);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
}
Now Once I pass the authentication (Which I kept dummy for initial level matching same username & password) & got the token & refresh token.
When I request for method that is decorated with [Authorize] attribute, I always gets 401 status code.
I testing this method in postman following way
Any help or guidance will be really appreciated. I have invested my two days finding the solution for this but all in vain.

Using Basic auth with OAuth2 and JHipster

we use jhipster to generate gateway and microservices with OAuth2 authentication, and that works fine with a JHipster Registry and a Keycloak server. But we have a microservice that will be called from an external service, and this service use basic authentication.
So, on the gateway, we need to send login and pasword from basic auth to keycloak server, and use access token to call our service. I get access token by adding filter in MicroserviceSecurityConfiguration class :
http.addFilterBefore(basicAuthFilter, UsernamePasswordAuthenticationFilter.class);
And here a extract of filter method :
ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
details.setAccessTokenUri("http://keycloakserver/auth/realms/jhipster/protocol/openid-connect/token");
details.setGrantType("password");
details.setClientId("clientId");
details.setClientAuthenticationScheme(AuthenticationScheme.form);
details.setUsername(login);
details.setPassword(password);
AccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();
ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(details, tokenRequest);
I guess I have to store this token in tokenStore, but I don't know how. So my questions are how use this token, and does the the way I get it is correct ?
Thanks for your help !
I Have same problem with you in my production system, then I changed Authentication server to JHipster UAA server, and problem is resolved.
I think you are using first kind now:
OAuth 2.0 / OIDC Authentication:
this uses an OpenID Connect server, like Keycloak or Okta, which handles authentication outside of the application.
Authentication with JHipster UAA server:
this uses a JHipster UAA server that must be generated separately, and which is an OAuth2 server that handles authentication outside of the application.
After some tests and trial and error, I managed to do that I want.
First, I created a BasicAuthenticationFilter class :
#Configuration
public class BasicAuthenticationFilter implements Filter {
private static final String GRANT_TYPE = "password";
private static final String BASIC_AUTH_HEADER = "Authorization";
private static final String BASIC_PREFIX = "Basic ";
#Value("${security.oauth2.client.access-token-uri}")
private String accessTokenUri;
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Autowired
private TokenStore tokenStore;
private ResourceOwnerPasswordAccessTokenProvider provider;
public BasicAuthenticationFilter( ) {
provider = new ResourceOwnerPasswordAccessTokenProvider();
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
header = httpRequest.getHeader(BASIC_AUTH_HEADER);
if (header != null && header.startsWith(BASIC_PREFIX)) {
String base64 = header.substring(BASIC_PREFIX.length());
String loginPassword = new String(Base64.getDecoder().decode(base64.getBytes()));
String[] split = loginPassword.split(":");
String login = split[0];
String password = split[1];
authenticate(httpRequest, login, password);
}
}
chain.doFilter(request, response);
}
private void authenticate(HttpServletRequest httpRequest, String login, String password) {
ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
details.setAccessTokenUri(accessTokenUri);
details.setGrantType(GRANT_TYPE);
details.setClientId(clientId);
details.setClientAuthenticationScheme(AuthenticationScheme.query);
details.setUsername(login);
details.setPassword(password);
DefaultAccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();
tokenRequest.setCurrentUri(httpRequest.getRequestURI());
try {
OAuth2AccessToken accessToken = provider.obtainAccessToken(details, tokenRequest);
OAuth2Authentication oauth2Authentication = tokenStore.readAuthentication(accessToken);
AccessTokenDetails accessTokenDetail = new AccessTokenDetails(accessToken.getValue());
oauth2Authentication.setDetails(accessTokenDetail);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(oauth2Authentication);
} catch (OAuth2AccessDeniedException e) {
throw new AccessDeniedException("Wrong credentials !");
}
}
#Override
public void destroy() {
}
public static class AccessTokenDetails {
private static final String DEFAULT_TOKEN_TYPE = "bearer";
public final String tokenType;
public final String tokenValue;
public AccessTokenDetails(String tokenValue) {
this(DEFAULT_TOKEN_TYPE, tokenValue);
}
public AccessTokenDetails(String tokenType, String tokenValue) {
this.tokenType = tokenType;
this.tokenValue = tokenValue;
}
}
}
This filter check if there are basic authentication, and if yes, authenticate user. The authentication details are stored in internal class AccessTokenDetails. So, the token can be read in AuthorizationHeaderUtil :
public class AuthorizationHeaderUtil {
public static String getAuthorizationHeader() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Object details = authentication.getDetails();
String tokenType = "";
String tokenValue = "";
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oauth2Details = (OAuth2AuthenticationDetails) details;
tokenType = oauth2Details.getTokenType();
tokenValue = oauth2Details.getTokenValue();
} else if (details instanceof AccessTokenDetails) {
AccessTokenDetails accessTokenDetails = (AccessTokenDetails) details;
tokenType = accessTokenDetails.tokenType;
tokenValue = accessTokenDetails.tokenValue;
}
return String.format("%s %s", tokenType, tokenValue);
}
}
This class has been generated by JHipster, I add a check for the two authentication detail class I use.
I hope that will be useful.
Denis

Web Api Bearer JWT Token Authentication with Api Key fails on successive calls after successful token authentication

I have created an MVC Core API that authenticates users with an api key. On successful authentication it sends back a JWT token which they use for any subsequent requests.
I can successfully get authenticated with a valid api key and get a token as a response back. Then i can use this token to make a request but the next request fails.
In my real application the consumer is an MVC Core site and until now i hadn't noticed this issue because in every mvc controller action i was calling one api action but now that i have the need to call two api actions one after the other on the same mvc action the second one fails and i cannot understand why.
I have reproduced my issue in a sample web api and console application.
This is the code for the MVC Core API endpoint validating the api key and generating the jwt token:
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using PureHub.Services.Models.Valid8.Authentication;
namespace BugApiJwt.Controllers
{
[Authorize]
[Route("v1/[controller]")]
public class AuthenticationController : ControllerBase
{
[AllowAnonymous]
[HttpPost("[action]")]
public virtual async Task<IActionResult> Token([FromBody] ApiLoginRequest model)
{
if (model != null)
{
if (model.ApiKey == "VdPfwrL+mpRHKgzAIm9js7e/J9AbJshoPgv1nIZiat22R")
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),
new Claim(JwtRegisteredClaimNames.Iat,
new DateTimeOffset(DateTime.UtcNow).ToUniversalTime().ToUnixTimeSeconds().ToString(),
ClaimValueTypes.Integer64)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("FTTaIMmkh3awD/4JF0iHgAfNiB6/C/gFeDdrKU/4YG1ZK36o16Ja4wLO+1Qft6yd+heHPRB2uQqXd76p5bXXPQ=="));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "http://localhost:58393/",
audience: "http://localhost:58393/",
claims: claims,
expires: DateTime.UtcNow.AddMinutes(30),
signingCredentials: creds);
return Ok(new ApiLoginResponse
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo
});
}
}
return BadRequest();
}
}
}
This is the protected resource:
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace BugApiJwt.Controllers
{
[Authorize]
[Route("v1/values")]
public class ValuesController : Controller
{
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpGet("{id}")]
public string Get(int id)
{
return $"You said: {id}";
}
}
}
And this is my startup:
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace BugApiJwt
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "http://localhost:58393/",
ValidAudience = "http://localhost:58393/",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("FTTaIMmkh3awD/4JF0iHgAfNiB6/C/gFeDdrKU/4YG1ZK36o16Ja4wLO+1Qft6yd+heHPRB2uQqXd76p5bXXPQ==")),
};
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
}
And this is the console application i'm testing it with:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace BugApiJwt.Console
{
public class Program
{
private const string ApiKey = "VdPfwrL+mpRHKgzAIm9js7e/J9AbJshoPgv1nIZiat22R";
private const string BaseAddress = "http://localhost:58393/";
private static HttpClient _client = new HttpClient();
private static string _realToken = string.Empty;
private static void Main()
{
_client = new HttpClient
{
BaseAddress = new Uri(BaseAddress)
};
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Works
System.Console.WriteLine("Call GetOne");
var getOne = Get().GetAwaiter().GetResult();
System.Console.WriteLine(getOne);
// Fails
System.Console.WriteLine("Call GetAll");
var getTwo = GetAll().GetAwaiter().GetResult();
System.Console.WriteLine(getTwo);
System.Console.WriteLine("All Finished. Press Enter to exit");
System.Console.ReadLine();
}
private static async Task<string> GetAuthenticationToken()
{
const string resource = "v1/authentication/token";
if (!string.IsNullOrEmpty(_realToken)){return _realToken;}
var loginRequest = new ApiLoginRequest{ApiKey = ApiKey};
var httpResponseMessage = await _client.PostAsync(resource, ObjectToJsonContent(loginRequest)).ConfigureAwait(false);
if (httpResponseMessage.IsSuccessStatusCode)
{
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var obj = JsonConvert.DeserializeObject<ApiLoginResponse>(content);
_realToken = obj.Token;
return obj.Token;
}
throw new Exception("Token is null");
}
public static async Task<string> Get()
{
var resource = "v1/values/1";
var token = await GetAuthenticationToken();
_client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {token}");
var httpResponseMessage = await _client.GetAsync(resource);
System.Console.WriteLine(httpResponseMessage.RequestMessage.Headers.Authorization);
System.Console.WriteLine(httpResponseMessage.Headers.WwwAuthenticate);
var content = await httpResponseMessage.Content.ReadAsStringAsync();
return content;
}
public static async Task<string> GetAll()
{
var resource = "v1/values";
var token = await GetAuthenticationToken();
_client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {token}");
var httpResponseMessage = await _client.GetAsync(resource);
System.Console.WriteLine(httpResponseMessage.RequestMessage.Headers.Authorization);
System.Console.WriteLine(httpResponseMessage.Headers.WwwAuthenticate);
var content = await httpResponseMessage.Content.ReadAsStringAsync();
return content;
}
private static StringContent ObjectToJsonContent<T>(T objectToPost) where T : class, new()
{
var tJson = JsonConvert.SerializeObject(objectToPost,
Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
return new StringContent(tJson, Encoding.UTF8, "application/json");
}
}
public class ApiLoginRequest
{
public string ApiKey { get; set; }
}
public class ApiLoginResponse
{
public string Token { get; set; }
public DateTime Expiration { get; set; }
}
}
Any help on why the second call fails?
The error message shown in the web api output window is:
Bearer was not authenticated. Failure message: No SecurityTokenValidator available for token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyNmFiNjQzYjFjOTM0MzYwYjI4NDAxMzZjNDIxOTBlZSIsImlhdCI6MTUxMDA2NDg0MywiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxIiwiR2xvYmFsSWQiOiI2NjVjYWEzYjYxYmY0MWRmOGIzMTVhODY5YzQzMmJkYyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJuYmYiOjE1MTAwNjQ4NDMsImV4cCI6MTUxMDA2NjY0MywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM2MCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNjAifQ.wJ86Ut2dmbDRDCNXU2kWXeQ1pQGkiVtUx7oSyJIZMzc
It doesn't work because this piece of code TryAddWithoutValidation("Authorization", $"Bearer {token}"); adds the token on top of what is already in the authorization header without clearing it first. As a result successive calls add the bearer string with the token in the header which already contains the bearer token.

Specify login_hint using .NET Google.Apis.Oauth2.v2

Since the Google.Apis.Oauth2.v2 in GoogleWebAuthorizationBroker.AuthorizeAsync() requests a uri to a static json file that contains the links and parameters to Google Oauth2 services, how can I specify the login_hint parameter if I happen to know that information ahead of time?
var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new Uri("ms-appx:///Assets/client_secret.json"),
myScopes,
"user",
CancellationToken.None);
Extract from client_secret.json:
"auth_uri":"https://accounts.google.com/o/oauth2/auth?login_hint=user#domain.com"
How to specify the login_hint parameteron a per user basis?
I ended up subclassing the Google web authorization broker like this:
public class MyOAuth2WebAuthorizationBroker : GoogleWebAuthorizationBroker
{
public static async Task<UserCredential> AuthorizeAsync(ClientSecrets clientSecrets,
IEnumerable<string> scopes, string user, CancellationToken taskCancellationToken)
{
var initializer = new MyOAuth2AuthorizationCodeFlow.Initializer
{
ClientSecrets = clientSecrets,
Scopes = scopes,
DataStore = new StorageDataStore(),
};
var installedApp = new AuthorizationCodeWindowsInstalledApp(new MyOAuth2AuthorizationCodeFlow(initializer, user));
return await installedApp.AuthorizeAsync(user, taskCancellationToken).ConfigureAwait(false);
}
public class MyOAuth2AuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
private string loginHint { get; set; }
public MyOAuth2AuthorizationCodeFlow(Initializer initializer, string loginHint) : base(initializer)
{
this.loginHint = loginHint;
}
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
{
return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
LoginHint = this.loginHint
};
}
}
}