I am writing an Windows App that will communicate with Web API. Here is how I am making a call :
HttpClient client = null;
HttpClientHandler handler = new HttpClientHandler() { PreAuthenticate = true, Credentials = CredentialCache.DefaultCredentials };
client = new HttpClient();
client.BaseAddress = new Uri(apiBaseAddress);
var byteArray = Encoding.ASCII.GetBytes(Environment.UserName);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
HttpResponseMessage response = client.GetAsync("api/Tickets/AuthenticateUser").Result;
I am passing the currently logged credentials. I have written a filter which connects to db and checks if the username exists. The code :
public class BasicAuthenticationWindowsAppAttribute : System.Web.Http.Filters..AuthorizationFilterAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization == null)
{
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
else
{
string authToken = actionContext.Request.Headers.Authorization.Parameter;
string Handle = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
GenericIdentity gi = new GenericIdentity(Handle);
Thread.CurrentPrincipal = new GenericPrincipal(gi, null);
HttpContext.Current.User = Thread.CurrentPrincipal;
Amo_MasterDataEntities amoMasterDataContext = new Amo_MasterDataEntities();
var query = from a in amoMasterDataContext.allassociatemasters
where a.Handle == Handle
select a;
//If Handle is present in AMOMasterData.AllAssociatemaster table
if (query.Count() > 0)
{
//TicketsController tc = new TicketsController();
string assocId = "", fName ="", lName = "";
bool authenticated = false;
foreach (var item in query)
{
assocId = item.AssociateID;
fName = item.FirstName;
lName = item.LastName;
authenticated = true;
}
AuthInfo info = new AuthInfo();
info.AssociateId = assocId;
info.FirstName = fName;
info.LastName = lName;
info.IsAuthenticated = authenticated;
actionContext.Request.Properties.Add(new KeyValuePair<string, object>("AuthInfo", info));
base.OnAuthorization(actionContext);
}
//else return error
else
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
}
}
When I run the web service in my local system it works. But when I deploy the web service on server it is giving me 401 Unauhorized Message.
I have enabled both Basic and Windows Authentication in IIS and Web.config contains <authentication mode="Windows" />
Edit :
I am able to access the Web API Methods from the server where I have deployed.
But When I am calling the Web API from a windows client on another Machine it is throwing 401 error.
Should I use CORS? If yes please let me know how?
Can anybody give me a solution for this.
I found the reason,..
I was not passing the handler to constructor of HttpClient that was the problem. So in above code replace :
client = new HttpClient();
with :
client = new HttpClient(handler);
Such a silly mistake. Sorry for the trouble.
Related
We are currently getting a list of our Users using MS Graph and the directoryObjects/getByIds endpoint.
In the Startup of the ASP NET Core API we are using Microsoft.IdentityModel.Clients.ActiveDirectory and this code
services.AddHttpClient("GraphApi", async hc =>
{
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/" + this.configuration["GraphApi:Tenant"]);
ClientCredential credential = new ClientCredential(this.configuration["GraphApi:ClientId"], this.configuration["GraphApi:ClientSecret"]);
hc.BaseAddress = new Uri($"https://graph.microsoft.com/v1.0/");
hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", credential);
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
});
I am creating a new Azure Function and need to do the same thing again. I was going to use the same code and Microsoft.IdentityModel.Clients.ActiveDirectory but that package has been deprecated and we should be using Microsoft.Identity.Client.
I can see lots of samples for various scenarios but they seem to be all calling the public MS Graph whereas I want to get the users from our own Azure B2C. Can someone point me at the right resources\demo.
The Azure Function will not be running in the context of a user so Managed Identity or Client Secret approach would be useful
I have implemented a similar kind of scenario for getting Azure AD user but different way in MVC
CODE
I have used these NuGet packages
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
Startup class
public class Startup
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // This is a simplification
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
},
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
HomeController
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge( new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
public void SignOut()
{
HttpContext.GetOwinContext().Authentication.SignOut( OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
ClaimsController
public ActionResult Index()
{
var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
ViewBag.Name = userClaims?.FindFirst("name")?.Value;
ViewBag.Username = userClaims?.FindFirst("preferred_username")?.Value;
ViewBag.Subject = userClaims?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
ViewBag.TenantId = userClaims?.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
return View();
}
I tried to cover all possible implementations. Hope it will work in your case
Thanks
a had successful connection with ExchangeVersion.Exchange2007_SP1, when I changed it on 2010 or latest I get the exception on this row:
service.AutodiscoverUrl(MailboxToAccess, RedirectionUrlValidationCallback);
->System.NullReferenceException: 'Object reference not set to an instance of an object.'
I tried with Packages:
Exchange.WebServices.Managed.Api v2.2.1.2 and Microsoft.Exchange.WebServices v2.2.0
static void Main(string[] args)
{
String MailboxToAccess = "username#domain";
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
SearchFilter sfSearchFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false);
service.Credentials = new WebCredential("username#domain", "password");
service.AutodiscoverUrl(MailboxToAccess, RedirectionUrlValidationCallback);
SetStreamingNotifications(service);
}
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
I set the Exchange service URL manually using
/ Create the binding.
ExchangeService service = new ExchangeService();
// Set the credentials for the on-premises server.
service.Credentials = new WebCredentials("user1#contoso.com", "password");
// Set the URL.
service.Url = new Uri("https://computername.domain.contoso.com/EWS/Exchange.asmx");
I still don't understand why It didn't work for me with
service.AutodiscoverUrl("User1#contoso.com");
I am using the HttpClient but my results are taking up to 6 seconds coming back from the same machine on the same subnet and ip range of 192.168. When I call the api directly from the ip address the results are more or less instant so why is it so slow with httpclient on the same computer.
I have seen other so's that suggest set to use proxy as false is the best way to go.
I have also tested this on a stock phone and it takes around 8 seconds for the login to be successful on the phone.
private HttpClient _client;
public async Task<String> Getusers()
{
var content = "";
HttpClientHandler hch = new HttpClientHandler();
hch.Proxy = null;
hch.UseProxy = false;
_client = new HttpClient(hch);
var uri = new Uri(Constants.ApiEndPoint + "/Users"); // Your url is here
try
{
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
throw ex;
}
return content;
}
Here is my login method in case anyone can see something wrong with it.
private async void BtnLogin_Clicked(object sender, EventArgs e)
{
string content = await Getusers(); //Sends a GET request to the specified Uri and returns the response body as a string in an asynchronous operation
List<Users> _users = JsonConvert.DeserializeObject<List<Users>>(content); //Deserializes or converts JSON String into a collection of Post
var userName = txtUserName.Text;
var password = txtPassword.Text;
var isValidUser = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault();
var driverId = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault().ID;
if (isValidUser != null)
{
Application.Current.Properties["driverId"] = driverId;
Application.Current.MainPage = new MainPage();
}
else
{
lblError.Text = "Error your credentials are invalid, please try again";
}
}
I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps
I am using JWT authentication for WEB API using OAuth 2. I am using Refresh tokens mechanism. I am able to generate the refresh token and call API service from it before the expiration time. Once the token is expired , i am calling service to issue new token using refresh token id. But its giving error in my CustomJWTFormat class UnProtect method as it not implement any logic. I am not getting what logic to be implemented to reissue JWT refresh token.
Sample codes for configuring serviec to use JSON web token format:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
AccessTokenFormat = new CustomJwtFormat(<issuer>),
RefreshTokenFormat = new CustomJwtFormat(<issuer>)
};
Sample code of my CustomJWTFormat class:
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private const string AudiencePropertyKey = "as:client_id";
private readonly string _issuer = string.Empty;
private string symmetricKeyAsBase64 = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;
if (string.IsNullOrWhiteSpace(audienceId))
{
audienceId = <audience>;
symmetricKeyAsBase64 = <secret key>;
}
else
{
using (AuthRepository _repo = new AuthRepository())
{
var audience = _repo.FindClient(audienceId);
symmetricKeyAsBase64 = audience.Secret;
}
}
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
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, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
///Need logic for this method. Its calling when service is called to generated new token for refresh id
public AuthenticationTicket Unprotect(string protectedText)
{
throw NotImplementedException();
}
}
}
Any help will be appreciated.
Have a look at this sample to give you an idea of validating a token.
https://github.com/AzureADSamples/WebAPI-ManuallyValidateJwt-DotNet/blob/master/TodoListService-ManualJwt
In particular, Global.asax.cs.