Service to Service call and Graph APi - office365api

I registered a web API https://dev.office.com/app-registration. I kept sign URL
localhost:8000 and RedirectURI as localhost:8000.
After this, I created a console application to access the graph API. Here is the code. But it doesnt work. I get the error "access denied".
Am I missing anything here?
One more thing, I checked the access_token and I dont see any user information there.
string clientId = "<>";
string clientsecret = "<>";
string tenant = "my.com";
var authUri = "https://login.microsoftonline.com/" + tenant + "/oauth2/token";
var RESOURCE_URL = "https://graph.microsoft.com";
HttpClient client = new HttpClient();
var authContext = new AuthenticationContext(authUri);
var credential = new ClientCredential(clientId: clientId, clientSecret: clientsecret);
var result = authContext.AcquireTokenAsync(RESOURCE_URL, credential).Result;
Console.WriteLine(result.AccessToken.ToString());
client.DefaultRequestHeaders.Add("Authorization", "bearer " + result.AccessToken);
var httpContent = new StringContent(content, Encoding.GetEncoding("utf-8"), "application/json");
var response = client.GetAsync("https://graph.microsoft.com/v1.0/users/ankushb/calendar/events").Result;
// var response = client.PostAsync("https://graph.microsoft.com/v1.0/groups", httpContent).Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);

There are two basic authentication flow for OAuth2 that Azure AD use.
First is authorization code grant flow which used to access to web APIs by native clients and websites.In this flow, the user delegates access to a client application.
Second is Client Credentials grant flow which permits a web service (a confidential client) to use its own credentials to authenticate when calling another web service, instead of impersonating a user. In this scenario, the client is typically a middle-tier web service, a daemon service, or web site.
The code above is using the Client Credentials grant flow, that's the reason there is no user information.
In your scenario, you should register an native app(console application is not confidential clients which should not use the secret to request for the token ) and use the authorization code grant flow to enable users sign-in with their account. And to use this flow in a console application, we need to prompt a web dialog navigate to the login-in page and get the auth code to request for the access token.
Here is an sample to authenticate the app with authorization code grant flow:
var oauth = new OauthConfiguration
{
Authority = "https://login.microsoftonline.com",
Tenant = "common",
ClientId = "{clientId}",
RedirectURI = "{redirectURL}",
Secret = ""
};
var tokenResponse = new OAuth2.OauthWebAuthHelper(oauth).AcquireTokenWithResource("https://graph.microsoft.com");
var accessToken = tokenResponse.GetValue("access_token").Value<string>();
var refreshToken = tokenResponse.GetValue("refresh_token").Value<string>();
namespace OAuth2
{
public class OauthWebAuthHelper
{
public enum Version
{
V1 = 1,
V2 = 2
}
private OauthConfiguration _configuration;
private const string OAUTH2_AUTHORIZE_V1_SUFFIX = #"oauth2/";
private const string OAUTH2_AUTHORIZE_V2_SUFFIX = #"oauth2/v2.0";
private string _authorizeSuffix;
public OauthWebAuthHelper(OauthConfiguration configuration, Version version = Version.V1)
{
_configuration = configuration;
switch (version)
{
case Version.V1: _authorizeSuffix = OAUTH2_AUTHORIZE_V1_SUFFIX; break;
case Version.V2: _authorizeSuffix = OAUTH2_AUTHORIZE_V2_SUFFIX; break;
}
}
public void LogOut()
{
var dialog = new WebBrowserDialog();
dialog.Open(string.Format("{0}/logout", EndPointUrl));
}
protected string EndPointUrl
{
get
{
return string.Format("{0}/{1}/{2}", _configuration.Authority, _configuration.Tenant, _authorizeSuffix);
}
}
public JObject GetAuthorizationCode()
{
JObject response = new JObject();
var parameters = new Dictionary<string, string>
{
{ "response_type", "code" },
{ "client_id", _configuration.ClientId },
{ "redirect_uri", _configuration.RedirectURI },
{ "prompt", "login"}
};
var requestUrl = string.Format("{0}/authorize?{1}", EndPointUrl, BuildQueryString(parameters));
var dialog = new WebBrowserDialog();
dialog.OnNavigated((sender, arg) =>
{
if (arg.Url.AbsoluteUri.StartsWith(_configuration.RedirectURI))
{
var collection = HttpUtility.ParseQueryString(arg.Url.Query);
foreach (var key in collection.AllKeys)
{
response.Add(key, collection[key]);
}
dialog.Close();
}
});
dialog.Open(requestUrl);
return response;
}
public JObject GetAuthorizationCode(string scope)
{
JObject response = new JObject();
var parameters = new Dictionary<string, string>
{
{ "response_type", "code" },
{ "client_id", _configuration.ClientId },
{ "redirect_uri", _configuration.RedirectURI },
{ "prompt", "login"},
{ "scope", scope}
};
var requestUrl = string.Format("{0}/authorize?{1}", EndPointUrl, BuildQueryString(parameters));
var dialog = new WebBrowserDialog();
dialog.OnNavigated((sender, arg) =>
{
if (arg.Url.AbsoluteUri.StartsWith(_configuration.RedirectURI))
{
var collection = HttpUtility.ParseQueryString(arg.Url.Query);
foreach (var key in collection.AllKeys)
{
response.Add(key, collection[key]);
}
dialog.Close();
}
});
dialog.Open(requestUrl);
return response;
}
public JObject AcquireTokenWithResource(string resource)
{
var codeResponse = GetAuthorizationCode();
var code = codeResponse.GetValue("code").Value<string>();
var parameters = new Dictionary<string, string>
{
{ "resource", resource},
{ "client_id", _configuration.ClientId },
{ "code", code},
{ "grant_type", "authorization_code" },
{ "redirect_uri", _configuration.RedirectURI},
{ "client_secret",_configuration.Secret}
};
var client = new HttpClient();
var content = new StringContent(BuildQueryString(parameters), Encoding.GetEncoding("utf-8"), "application/x-www-form-urlencoded");
var url = string.Format("{0}/token", EndPointUrl);
var response = client.PostAsync(url, content).Result;
var text = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject(text) as JObject;
}
public JObject RefreshTokenWithResource(string refreshToken)
{
var parameters = new Dictionary<string, string>
{
{ "client_id", _configuration.ClientId },
{ "refresh_token", refreshToken},
{ "grant_type", "refresh_token" }
};
var client = new HttpClient();
var content = new StringContent(BuildQueryString(parameters), Encoding.GetEncoding("utf-8"), "application/x-www-form-urlencoded");
var url = string.Format("{0}/token", EndPointUrl);
var response = client.PostAsync(url, content).Result;
var text = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject(text) as JObject;
}
public JObject AcquireTokenWithScope(string scope)
{
var codeResponse = GetAuthorizationCode(scope);
var code = codeResponse.GetValue("code").Value<string>();
var parameters = new Dictionary<string, string>
{
{ "client_id", _configuration.ClientId },
{ "code", code},
{ "grant_type", "authorization_code" },
{ "redirect_uri", _configuration.RedirectURI},
};
var client = new HttpClient();
var content = new StringContent(BuildQueryString(parameters), Encoding.GetEncoding("utf-8"), "application/x-www-form-urlencoded");
var url = string.Format("{0}/token", EndPointUrl);
var response = client.PostAsync(url, content).Result;
var text = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject(text) as JObject;
}
private string BuildQueryString(IDictionary<string, string> parameters)
{
var list = new List<string>();
foreach (var parameter in parameters)
{
if (!string.IsNullOrEmpty(parameter.Value))
list.Add(string.Format("{0}={1}", parameter.Key, HttpUtility.UrlEncode(parameter.Value)));
}
return string.Join("&", list);
}
}
public class OauthConfiguration
{
public string Authority { get; set; }
public string Tenant { get; set; }
public string ClientId { get; set; }
public string RedirectURI { get; set; }
public string Secret { get; set; }
}
public class WebBrowserDialog
{
private const int DEFAULT_WIDTH = 400;
private const int DEFAULT_HEIGHT = 500;
private Form _displayLoginForm;
private string _title;
private WebBrowser _browser;
private WebBrowserNavigatedEventHandler _webBrowserNavigatedEventHandler;
public WebBrowserDialog()
{
_title = "OAuth Basic";
_browser = new WebBrowser();
_browser.Width = DEFAULT_WIDTH;
_browser.Height = DEFAULT_HEIGHT;
_browser.Navigated += WebBrowserNavigatedEventHandler;
_displayLoginForm = new Form();
_displayLoginForm.SuspendLayout();
_displayLoginForm.Width = DEFAULT_WIDTH;
_displayLoginForm.Height = DEFAULT_HEIGHT;
_displayLoginForm.Text = _title;
_displayLoginForm.Controls.Add(_browser);
_displayLoginForm.ResumeLayout(false);
}
public void OnNavigated(WebBrowserNavigatedEventHandler handler)
{
_webBrowserNavigatedEventHandler = handler;
}
protected void WebBrowserNavigatedEventHandler(object sender, WebBrowserNavigatedEventArgs e)
{
if(_webBrowserNavigatedEventHandler != null)
{
_webBrowserNavigatedEventHandler.Invoke(sender, e);
}
}
public void Open(string url)
{
_browser.Navigate(url);
_displayLoginForm.ShowDialog();
}
public void Close()
{
_displayLoginForm.Close();
}
}
}
And you can get more samples about Microsoft Graph from here.

Related

Getting Error 400: invalid_request while redirecting Authorization page

Following this guide (https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-mvc) I made the code work in localhost but it gives an Error 400: invalid_request device_id and device_name are required for private IP: http://xx.xx.xx.xx:xxxxx/AuthCallback/IndexAsync while redirecting to Authorization page. We added subdomain in dns for our windows server ip address and registered the same subdomain in google api console. Why google gets ip address of the server instead of subdomain? Is the problem related to dns?
These are the working codes on local machine:
HomeController.cs
public class HomeController : Controller
{
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "BPM Mail"
});
var gmailProfile = service.Users.GetProfile("me").Execute();
var userGmailEmail = gmailProfile.EmailAddress;
Utils.userMail = userGmailEmail;
Utils.cred = result.Credential;
// SAMPLE CODE:
//var list = await service.Files.List().ExecuteAsync();
//ViewBag.Message = "FILE COUNT IS: " + list.Items.Count();
return new RedirectResult("~/Apps/Mail/Default.aspx");
}
else
{
return new RedirectResult(result.RedirectUri);
}
}
}
AppFlowMetadata.cs
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "ClientId ",
ClientSecret = "ClientSecret "
},
Scopes = new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.MailGoogleCom, GmailService.Scope.GmailModify },
DataStore = new FileDataStore("D:/bpm_mail/mytokens/token.json", true)
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
//Utils.userId = user.ToString();
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
//public override string AuthCallback
//{
// get { return #"/AuthCallback/IndexAsync"; }
//}
}
AuthCallbackController.cs
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
{
get { return new AppFlowMetadata(); }
}
}
Issue solved by changing private ip to public one and redirect it to domain.

.Net Core JWT + cookie auth on Web API and MVC Web Project invalid signature

My application first uses cookie auth to login. I then have it calling an API which I would like to use JWT auth which opens a websocket stream (Im debating and putting this stream on the UI) when the API gets an update it calls the UI and I want this one call to be a JWT Auth, I tried a bunch of things this past weekend but I keep getting invalid signature or SSL cert is invalid.
Both projects are .Net Core. One is a web api and the other is a MVC project
Its actually 2 APIS and MVC (userAPI, TraderAPI and the UI)
User API
public string GenerateJWTToken(UserRoleVM user)
{
if (user != null)
{
var secretKey = Base64UrlEncoder.DecodeBytes(_config.GetSection("Tokens:Key").ToString());
var securityKey = new SymmetricSecurityKey(secretKey);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var currentTime = DateTime.Now;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName)
,new Claim(JwtRegisteredClaimNames.Sid, user.UserGUID)
,new Claim(JwtRegisteredClaimNames.NameId, user.UserID.ToString())
,new Claim(JwtRegisteredClaimNames.Email, user.Email)
,new Claim(JwtRegisteredClaimNames.AuthTime, currentTime.ToString())
,new Claim(ClaimTypes.Role, user.Role)
//,new Claim(JwtRegisteredClaimNames.)
,new Claim(type: "Subscribed", value: user.IsSubscribed.ToString())
,new Claim(type: "SubscribedDate", value: user.DateSubscribed.ToString())
};
var jwtSecurityToken = new JwtSecurityToken
(
signingCredentials: signingCredentials,
audience: _config.GetValue<string>("Tokens:Audience"),
issuer: _config.GetValue<string>("Tokens:Issuer"),
expires: currentTime.AddMinutes(_config.GetValue<int>("Tokens:LifeTime")),
//notBefore: currentTime,
claims: claims
);
return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
return null;
}
//public Task<LoginResponseVM> Login(string username, string password)
public async Task<string> Login(string username, string password)
{
string jwtToken = null;
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
try
{
var result = await _signInManager.PasswordSignInAsync(username, password, false, false);
if (result.Succeeded)
{
//var savedUser = await _userService.GetUserByNameAsync(username);
//var role = await _userService.GetUserRolesAsync(savedUser);
var userVM = await _userService.GetUserNRole2Async(username);
//jwtToken = Convert.ToBase64String(Encoding.ASCII.GetBytes(GenerateJWTToken(userVM)));
jwtToken = GenerateJWTToken(userVM);
}
}
catch (Exception ex)
{
}
}
return jwtToken;
}
TraderAPI
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpPost]
//public async Task<IActionResult> GetTradeStatusUpdate([FromBody] TradeResultVM model)
public async Task<IActionResult> SendTradeStatusUpdate([FromBody] TradeResultVM model)
{
_logger.LogInformation("Inside GetTradeStatus");
var lstTradeStatuses = await GetLstTradeStatuses();
if (lstTradeStatuses != null)
{
await _semaphoreSlimS.WaitAsync();
try
{
foreach (var activeTrade in lstTradeStatuses)
{
if (activeTrade.TradeID == model.TradeID)
{
_logger.LogInformation("Trade found in lstTradeStatuses");
...
}
[HttpPost("SendUITradeStatusV4")]
public async Task<IActionResult> SendUITradeStatusV4(BinanceStreamOrderUpdate orderUpdate, string token)
{
if (orderUpdate != null)
{
TradeResultVM tradeVM = new TradeResultVM
{
//TradeName = "Test",
TradeID = orderUpdate.OrderId,
TradeSymbol = orderUpdate.Symbol,
Price = orderUpdate.Price,
Quantity = orderUpdate.Quantity
};
string trade = JsonSerializer.Serialize(tradeVM);//
using var client = _httpClient.CreateClient("TraderUI");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
//client.BaseAddress = new Uri("https://localhost:50111");
//using var response = await client.PostAsync($"/HarkinsTrader/GetTradeStatusUpdate/",
_logger.LogInformation("Sending Key");
using var response = await client.PostAsync($"/HarkinsTrader/SendTradeStatusUpdate/",
new StringContent(trade, Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Success StreamStatus Update");
return Ok();
}
}
return BadRequest();
}

JWT Authentication ASP.NET Core MVC application

I've seen numerous examples of how to use JWT authentication with Angular, React, Vue etc... clients but can't find any examples of using JWT authentication with ASP.NET Core (specifically 2.2) Web App Mvc.
Does anyone have any examples or advice on how to do this?
Thanks,
You can use this class based on nuget package JWT 3.0.3
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Newtonsoft.Json;
using System;
namespace Common.Utils
{
public class JwtToken
{
private IJwtEncoder encoder;
private IJwtDecoder decoder;
/// <remarks>
/// This requires a key value randomly generated and stored in your configuration settings.
/// Consider that it is a good practice use keys as at least long as the output digest bytes
/// length produced by the hashing algorithm used. Since we use an HMAC-SHA-512 algorithm,
/// then we can provide it a key at least 64 bytes long.
/// <see cref="https://tools.ietf.org/html/rfc4868#page-7"/>
/// </remarks>
public string SecretKey { get; set; }
public JwtToken()
{
IJwtAlgorithm algorithm = new HMACSHA512Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider datetimeProvider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, datetimeProvider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
decoder = new JwtDecoder(serializer, validator, urlEncoder);
SecretKey = "";
}
public JwtToken(string secretKey) : this()
{
SecretKey = secretKey;
}
public bool IsTokenValid(string token)
{
return !string.IsNullOrWhiteSpace(DecodeToken(token));
}
public string GetToken(object payload)
{
try
{
return encoder.Encode(payload, SecretKey);
}
catch (Exception)
{
return encoder.Encode(new DataModel(payload), SecretKey);
}
}
public string DecodeToken(string token)
{
try
{
if (string.IsNullOrWhiteSpace(token) || token == "null")
{
return null;
}
return decoder.Decode(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
}
public T DecodeToken<T>(string token) where T : class
{
try
{
if (string.IsNullOrWhiteSpace(token))
{
return null;
}
return decoder.DecodeToObject<T>(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
catch (Exception)
{
var data = decoder.DecodeToObject<DataModel>(token, SecretKey, true).Data;
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(data));
}
}
}
public class DataModel
{
public DataModel(object data)
{
Data = data;
}
public object Data { get; set; }
}
}
Then in your Startup class Configure method set the jwt middleware
for check authentication status of each request:
app.Use((context, next) =>
{
// verify app access token if not another service call
var appAccessToken = context.Request.Headers["Authorization"];
if (appAccessToken.Count == 0)
{
context.Items["User"] = null;
}
else
{
var token = appAccessToken.ToString().Replace("Bearer ", "");
var jwtToken = new JwtToken(config.JwtTokenSecret); //you need a secret (with requirements specified above) in your configuration (db, appsettings.json)
if (string.IsNullOrWhiteSpace(token) || !jwtToken.IsTokenValid(token))
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
dynamic user = jwtToken.DecodeToken<dynamic>(token);
var cachedToken = cache.Get(user.Id); //you need some cache for store your token after login success and so can check against
if (cachedToken == null || cachedToken.ToString() != token)
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
context.Items["User"] = new Dictionary<string, string>() {
{ "FullName",user.Name?.ToString()},
{ "FirstName",user.FirstName?.ToString()},
{ "LastName",user.LastName?.ToString()},
{ "Role",user.Role?.ToString()},
{ "Email",user.Email?.ToString()}
};
}
return next();
});
And finally you need generate the token and return it after
authentication:
[AllowAnonymous]
public IActionResult Login(string username, string password)
{
User user = null; //you need some User class with the structure of the previous dictionary
if (checkAuthenticationOK(username, password, out user)) //chackAuthenticationOk sets the user against db data after a succesfull authentication
{
var token = new JwtToken(_config.JwtTokenSecret).GetToken(user); //_config is an object to your configuration
_cache.Set(user.id, token); //store in the cache the token for checking in each request
return Ok(token);
}
return StatusCode(401, "User is not authorized");
}
Add following code to startup
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Issuer"],
ValidAudience = Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SigningKey"]))
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
}
Code for login action in AccountController
[Route("api/[controller]")]
public class AccountController : Controller
{
[AllowAnonymous]
[HttpPost]
[Route("login")]
public IActionResult Login([FromBody]LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var user = _userService.Authenticate(loginViewModel);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, loginViewModel.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken
(
issuer: _configuration["Issuer"],
audience: _configuration["Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(10),
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SigningKey"])),
SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_in = (int)token.ValidTo.Subtract(DateTime.UtcNow).TotalSeconds,// TimeSpan.FromTicks( token.ValidTo.Ticks).TotalSeconds,
sub = loginViewModel.Username,
name = loginViewModel.Username,
fullName = user.FullName,
jobtitle = string.Empty,
phone = string.Empty,
email = user.EmailName,
});
}
}
}
I assume you have implemented JWT on the server side. To handle this on client side, first you have to add token to web browser local storage. Add to your main layout javascript (let's named it AuthService.js)
below code adds token to local storage after login button clicked. gettokenfromlocalstorage() retrieve token from local storage.
<script>
var token = "";
function Loginclick() {
var form = document.querySelector('form');
var data = new FormData(form);
var authsevice = new AuthService();
authsevice.LogIn(data.get("username").toString(), data.get("password").toString());
}
function gettokenfromlocalstorage() {
var authserv = new AuthService();
var mytoken = authserv.getAuth();
authserv.LogOut();
}
var AuthService = /** #class */ (function () {
function AuthService() {
this.authKey = "auth";
}
AuthService.prototype.LogIn = function (username, password) {
this.username = username;
this.password = password;
this.grant_type = "password";
this.client_id = "MyClientId";
var loginurl = "/api/Token/Auth";
var xhr = new XMLHttpRequest();
xhr.open("POST", loginurl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(this));
xhr.onreadystatechange = function () {
console.log("onreadystatechange");
};
xhr.onerror = function () {
var aaa = this.responseText;
};
xhr.onload = function () {
var data = JSON.parse(this.responseText);
var auth = new AuthService();
auth.setAuth(data);
};
};
AuthService.prototype.LogOut = function () {
this.setAuth(null);
return true;
};
AuthService.prototype.setAuth = function (auth) {
if (auth) {
localStorage.setItem(this.authKey, JSON.stringify(auth));
}
else {
localStorage.removeItem(this.authKey);
}
return true;
};
AuthService.prototype.getAuth = function () {
var i = localStorage.getItem(this.authKey);
return i === null ? null : JSON.parse(i);
};
AuthService.prototype.isLoggedIn = function () {
return localStorage.getItem(this.authKey) !== null ? true : false;
};
return AuthService;
}());
var aa = new AuthService();
var gettoken = aa.getAuth();
if (gettoken !== null) {
token = gettoken.token;
}
</script>
To add token to the header of each anchor tag put below script also to
your main layout.
<script>
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function check() {
addheader(this.href);
return false;
}
}
function addheader(object) {
let xhr = new XMLHttpRequest();
xhr.open("GET", object, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send(null);
xhr.onload = function () {
window.history.pushState("/", "", xhr.responseURL);
//mycontainer is a div for parialview content
$("#mycontainer").html(xhr.responseText);
window.onpopstate = function (e) {
if (e.state) {
$("html").html = e.state;
document.title = e.state.pageTitle;
}
};
};
}
</script>
Remember that using of this approach, each view has to be loaded as a partial view.
If you insert url address in a web browser bar directly this solution doesn't work. I haven't figured it out yet. That's why to manage token authentication is better using single page application, not multipage application.
You can use this boilerplate to understand how to implement JWT tokenization with .Net Core. In the project you can find JWT, Swagger and EF features.

web api authentication from client side

i have a www.api.com and a www.client.com
all registration will be done at api.com and login will be done at api.com. client.com will only be able to see the UI of the login form.
after user login and api.com return a token to user. How to i use the token to access the rest of the webapi in the api.com? i want to access the GetExployeeByID method. after use login. i stored the token in the sessionStorage.setItem('token', data.access_token)
api method
[RoutePrefix("api/Customer")]
public class CustomerController : ApiController
{
List<customer> list = new List<customer>() { new customer {id=1 ,customerName="Marry",age=13},
new customer { id = 2, customerName = "John", age = 24 } };
[Route("GetExployeeByID/{id:long}")]
[HttpGet]
[Authorize]
public customer GetExployeeByID(long id)
{
return list.FirstOrDefault(x=>x.id==id);
}
}
update 1
this is my ajax post to call the api after login
function lgetemp() {
$.ajax({
url: 'http://www.azapi.com:81/api/customer/GetExployeeByID/1',
datatype:"json",
type: 'get',
headers: {
"access_token":sessionStorage.getItem("token")
},
crossDomain: true,
success: function (data) {
debugger
alert(data.customerName)
},
error: function (err) {
debugger
alert('error')
}
})
}
You should pass the token in the header of the request from the client to the api
Authorization Basic yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY=
The from your API you can query the headers and pull out the token.
string authorizationHeader = HttpContext.Current.Request.Headers["Authorization"];
string toke = authorizationHeader.Replace("Bearer ", String.Empty);
What I've done on my latest project is have a class AuthToken that does a lot of this for me
public class AuthToken : IAuthToken
{
private string _raw;
private IDictionary<string, string> _deserialized;
public string Raw
{
get
{
if (String.IsNullOrWhiteSpace(_raw))
{
string authorizationHeader = HttpContext.Current.Request.Headers["Authorization"];
_raw = authorizationHeader.Replace("Bearer ", String.Empty);
}
return _raw;
}
}
public IDictionary<string, string> Deserialized
{
get
{
if (_deserialized == null)
{
string[] tokenSplit = Raw.Split('.');
string payload = tokenSplit[1];
byte[] payloadBytes = Convert.FromBase64String(payload);
string payloadDecoded = Encoding.UTF8.GetString(payloadBytes);
_deserialized = JsonConvert.DeserializeObject<IDictionary<string, string>>(payloadDecoded);
}
return _deserialized;
}
}
}
Then I inject that into a UserContext class that I can inject into my controllers etc. The user context can then pull out claims from the token as needed. (assuming its a JWT)
public class UserContext : IUserContext
{
private IList<Claim> _claims;
private string _identifier;
private string _email;
private string _clientId;
public IAuthToken Token { get; }
public IList<Claim> Claims
{
get
{
return _claims ?? (_claims = Token.Deserialized.Select(self => new Claim(self.Key, self.Value)).ToList());
}
}
public string Identifier => _identifier ?? (_identifier = Token.Deserialized.ContainsKey("sub") ? Token.Deserialized["sub"] : null);
public string Email => _email ?? (_email = Token.Deserialized.ContainsKey(ClaimTypes.Email) ? Token.Deserialized[ClaimTypes.Email] : null);
public UserContext(IAuthToken authToken)
{
Token = authToken;
}
}
You need to pass the token to the request header and make the call to the API url. Below function can be called by passing the URL and token which you have.
static string CallApi(string url, string token)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient())
{
if (!string.IsNullOrWhiteSpace(token))
{
var t = JsonConvert.DeserializeObject<Token>(token);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
}
var response = client.GetAsync(url).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
Refer- Token based authentication in Web API for a detailed explanation.

app.UseIdentityServerBearerTokenAuthentication and UserInfo

For my WebAPI, I'm using:
public void ConfigureAuth( IAppBuilder app )
{
app.UseIdentityServerBearerTokenAuthentication( new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings[ "ida:Authority" ],
RequiredScopes = new[ ]
{
"XXXXAPI"
}
} );
}
I authenticate okay, but I need to get my roles and other information supplied by the UserInfo endpoint.
Does UseIdentityServerBearerTokenAuthentication do this automatically or is there an event like OpenIdConnectAuthenticationNotifications.AuthroizationCodeReceived that I should be using to set the ClaimsIdentity?
app.UseIdentityServerBearerTokenAuthentication does set the roles and scopes automatically.
For some unkown reason, I only returned data from the GetProfileDataAsync of the UserService only when the caller was UserInfoEndpoint. Once I got rid of this piece of code, all the roles were automatically populated.
You must use OAuth2+JWT and some custom configuration to save user roles and other claims in access token.
Add these values in Web.config
<appSettings>
<add key="as:AudienceId" value="414e1927a3884f68abc79f7283837fd1" />
<add key="as:AudienceSecret" value="qMCdFDQuF23RV1Y-1Gq9L3cF3VmuFwVbam4fMTdAfpo" />
</appSettings>
Write a CustomJwtFormat class
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using System;
using System.Configuration;
using System.IdentityModel.Tokens;
using Thinktecture.IdentityModel.Tokens;
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
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;
}
}
Create a custom authenticationProvider
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthRepository _repo = new AuthRepository())
{
User user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("unique_name", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
Configure Your Custom Setting
private static 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 ApplicationOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://localhost/")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private static void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = "http://localhost/";
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as: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)
}
});
}
public void Configuration(IAppBuilder app(
{
app.UseAutofacMvc();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
//other codes
}
If you want to get userInfo from access token, do it as follows:
public static string GetUserNameFromOAuth(HttpRequestMessage Request)
{
if (Request.Headers.Contains("Authorization"))
{
var authHeader = Request.Headers.GetValues("Authorization");
var authEncoded = authHeader.FirstOrDefault();
var authList = authEncoded.Split(' ');
var payload = authList[1];
var symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
string token = JWT.JsonWebToken.Decode(payload, keyByteArray);
var jsonObject = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(token);
var userName = jsonObject.FirstOrDefault(p => p.Key == "unique_name").Value;
return userName;
}
return "";
}
Test in postman:

Resources