webapi with basic authorize - asp.net-mvc

I've created a WebApi with .NET.
I want to secure some methods.
I would like only the users in aspnetuser table can execute this methods
I've created as
public class BasicAuthHttpModule : IHttpModule
and in the web.config I configured this as
<system.webServer>
<modules>
<add name="BasicAuthHttpModule" type="WebHostBasicAuth.Modules.BasicAuthHttpModule"/>
</modules>
</system.webServer>
And my BasicAuthHttpModule.cs:
I check the email and password with the aspnetusers tabla
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using ServicioProx360.Models;
using ServicioProx360;
namespace WebHostBasicAuth.Modules
{
public class BasicAuthHttpModule: IHttpModule
{
private const string Realm = "WebAPI Authentication";
public void Init(HttpApplication context)
{
// Se registran los manejadores de los eventos
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
private static bool AuthenticateUser(string credentials)
{
var encoding = Encoding.GetEncoding("iso-8859-1");
credentials = encoding.GetString(Convert.FromBase64String(credentials));
var credentialsArray = credentials.Split(':');
var username = credentialsArray[0];
var password = credentialsArray[1];
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
ApplicationUser usuario = manager.Find(username, password);
/* Aquí se validan las credenciales o el token enviado en el encabezado de la solicitud */
// if (!(username == "test" && password == "test"))
if (usuario==null)
{
return false;
}
var identity = new GenericIdentity(usuario.UserName);
SetPrincipal(new GenericPrincipal(identity, new string[] { "CIUDADADNO" }));
return true;
}
private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader != null)
{
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
// Se valida si el esquema de autenticación es básico (Basic Authentication)
if (authHeaderVal.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) && authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
}
// Si la solicitud no fue aprobada, se agrega el encabezado WWW-Authenticate a la respuesta
private static void OnApplicationEndRequest(object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
but it doen't work fine. if method is [authorize], it ask me for the email and password,I put correct data, but it ask again, again,again.
Can you help me?
Thank you very much

Take a look at this tutorial, it explains pretty well.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/basic-authentication

Related

AcquireTokenAsync timeouts Sometimes after some days #AzureActiveDirectory #ASP.NET MVC

I have followed all related threads for deadlocking the AcquireTokenAsync operation in ASP.NET MVC. But still I am facing the timeout issue - sometimes after 1 day, sometimes after 3 days. When I restart my web app all works fine again.
Here is my Token Bearer Class which retrieves the token:
public static class SSASTokenBearer
{
public static string Token = string.Empty;
public static DateTime TokenExpiryTime = DateTime.MinValue;
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
public static bool isTokenGenerated = false;
public static int _counter = 0;
public static bool IsThirdAttempt { get; set; }
public static List<string> lstToken = new List<string>();
public async static Task<string> GetAppOnlyAccessToken(string domain, string resourceUrl, string clientId, string clientSecret, string authUrl)
{
if (TokenExpiryTime > DateTime.UtcNow)
{
//if (_counter.Equals(Convert.ToInt32(Attempt.First)))
//{
// isTokenGenerated = false;
//}
return Token;
}
else
{
await semaphoreSlim.WaitAsync();
//ClearTokenListAndAttemptCounter();
try
{
if (TokenExpiryTime < DateTime.UtcNow)
{
_counter++;
var authority = $"{authUrl}/{domain}/oauth2/token";
var authContext = new AuthenticationContext(authority);
// Config for OAuth client credentials
var clientCred = new ClientCredential(clientId, clientSecret);
try
{
AuthenticationResult authenticationResult = await authContext.AcquireTokenAsync(resourceUrl, clientCred).ConfigureAwait(false);
//get access token
TokenExpiryTime = authenticationResult.ExpiresOn.DateTime;
Token = authenticationResult.AccessToken;
//lstToken.Add(Token);
//isTokenGenerated = true;
}
catch (AdalException ex)
{
throw ex;
}
}
}
finally
{
semaphoreSlim.Release();
}
}
return Token;
}
}
Here is the actual calling of the Bearer Token Class in the Open() method
using Microsoft.AnalysisServices.AdomdClient;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BMIS2.Data.Repositories.PreventativeMaintenance.Dax
{
public enum Attempt
{
First = 1,
Second = 2
}
public abstract class AbstactDal
{
public readonly string BMIS2DataBaseAzureSSAS = ConfigurationManager.AppSettings["BMIS2DataBaseAzureSSAS"];
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
//change the connection password and url dynamically and use initial catalog from web.config
private static string AzureSSASClientId;
private static string AzureSSASClientSecret;
private static string AzureSSASDomain;
private static string AzureSSASURL = Helper.AzureSSASURL;
private static string AzureAuthUrl;
protected AdomdConnection DaxConnection = null;
public AdomdCommand DaxCommand = null;
private static readonly object padlock = new object();
//private static Task<string> tskToken = null;
private bool switchConnection = Convert.ToBoolean(ConfigurationManager.AppSettings["SwitchConnection"]);
private static string ConnectionStr = ConfigurationManager.AppSettings["BMIS2DataBaseAzureSSASStatic"];
//public async Task Execute(string query, Func<T> MethodName)
public async Task ExecuteQuery(string query, Action MethodName)
{
if (switchConnection)
{
await Open(ConnectionStr);
}
else
{
await Open($"Provider=MSOLAP;Data Source={AzureSSASURL};Initial Catalog={Helper.SSASDB};User ID=;Password={await Init()};Persist Security Info=True;Impersonation Level=Impersonate");
}
await ExecuteDaxReader(query, MethodName);
Close();
}
private async Task<string> Init()
{
AzureSSASClientId = Helper.AzureSSASClientId;
AzureSSASClientSecret = Helper.AzureSSASClientSecret;
AzureSSASDomain = Helper.AzureSSASDomain;
AzureAuthUrl = Helper.AzureAuthUrl;
var token= await SSASTokenBearer.GetAppOnlyAccessToken(AzureSSASDomain, $"https://{Helper.AzureSSASZone}", AzureSSASClientId, AzureSSASClientSecret, AzureAuthUrl);
return token;
}
private async Task Open(string BMIS2DataBaseAzureSSAS)
{
DaxConnection = new AdomdConnection(BMIS2DataBaseAzureSSAS);
try
{
DaxConnection.Open();
}
catch (Exception ex)
{
Log.Warn(ex.Message.ToString());
await Open($"Provider=MSOLAP;Data Source={AzureSSASURL};Initial Catalog={Helper.SSASDB};User ID=;Password={ await Init()};Persist Security Info=True;Impersonation Level=Impersonate");
}
DaxCommand = new AdomdCommand();
DaxCommand.Connection = DaxConnection;
}
private void Close()
{
DaxConnection.Close();
}
public abstract Task ExecuteDaxReader(string query, Action MethodName);
}
}
In the implementation repository, each repository has its own common method to execute and read data from the data reader. We retrieve the DAX query from sql db and hit the same query to the SSAS Server.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using BMIS2.Common.CacheProviders;
using BMIS2.Data.Repositories.PreventativeMaintenance.Dax;
using BMIS2.Entity.ProcessPerformance;
using Microsoft.AnalysisServices.AdomdClient;
namespace BMIS2.Data.Repositories.PreventativeMaintenance.Imp
{
public class DayOfTheWeekRepository : AbstactDal, IDayOfTheWeekRepository
{
public readonly string BMIS2DataBase = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public readonly int DefaultQuerySessionTime = Convert.ToInt32(ConfigurationManager.AppSettings["DefaultQuerySessionTime"]);
private readonly ICacheProvider _cacheProvider;
private List<AbstractProcessPerformanceDayOfTheWeek> lstRoCont = null;
private bool IsROCount=false;
public DayOfTheWeekRepository(ICacheProvider cacheProvider)
{
_cacheProvider = cacheProvider;
}
public void GetIsRoCount()
{
try
{
using (var reader = DaxCommand.ExecuteReader())
{
while (reader.Read())
{
IsROCount = ((reader["FACT_ROSale[RoCount]"] == null || reader["FACT_ROSale[RoCount]"].ToString() == "") ? 0 : Convert.ToInt32(reader["FACT_ROSale[RoCount]"])) > 0 ? true : false;
}
}
}
catch(Exception ex)
{
Log.Error(ex.Message.ToString());
throw ex;
}
}
public static bool HasValue( double value)
{
return !Double.IsNaN(value) && !Double.IsInfinity(value);
}
public void GetResultForRoCount()
{
try
{
lstRoCont = new List<AbstractProcessPerformanceDayOfTheWeek>();
if (IsROCount)
{
using (var reader = DaxCommand.ExecuteReader())
{
while (reader.Read())
{
lstRoCont.Add(new ROCount()
{
DayOfTheWeek = (reader["[Day of Week]"] == null || reader["[Day of Week]"].ToString() == "") ? "" : Convert.ToString(reader["[Day of Week]"]),
TestCount = (reader["[Test Count]"] == null || reader["[Test Count]"].ToString() == "") ? 0 : Convert.ToInt32(reader["[Test Count]"]),
RoCount = (reader["[RO Count]"] == null || reader["[RO Count]"].ToString() == "") ? 0 : Convert.ToInt32(reader["[RO Count]"]),
RoTestedPercent = Math.Round((reader["[RO Tested %]"] == null || reader["[RO Tested %]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[RO Tested %]"]),1)
//RoTestedPercent = HasValue(Math.Round((((reader["[Test Count]"] == null || reader["[Test Count]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[Test Count]"])) / ((reader["[RO Count]"] == null || reader["[RO Count]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[RO Count]"]))) * 100, 1)) ? Math.Round(((reader["[Test Count]"] == null || reader["[Test Count]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[Test Count]"])) / ((reader["[RO Count]"] == null || reader["[RO Count]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[RO Count]"])) * 100, 1) : 0,
});
}
}
}
else
{
using (var reader = DaxCommand.ExecuteReader())
{
while (reader.Read())
{
lstRoCont.Add(new NoROCount()
{
DayOfTheWeek = (reader["[Day of Week]"] == null || reader["[Day of Week]"].ToString() == "") ? "" : Convert.ToString(reader["[Day of Week]"]),
//TotalCountPercent = HasValue(totalSum)? Math.Round((totalSum * 100),1) : 0,
TotalCountPercent = Math.Round((reader["[Test Count %]"] == null || reader["[Test Count %]"].ToString() == "") ? 0 : Convert.ToDouble(reader["[Test Count %]"]), 1),
TestCount = (reader["[Test Count]"] == null || reader["[Test Count]"].ToString() == "") ? 0 : Convert.ToInt32(reader["[Test Count]"])
});
}
}
}
}
catch (Exception ex)
{
Log.Error(ex.Message.ToString());
throw ex;
}
}
public async Task<List<AbstractProcessPerformanceDayOfTheWeek>> GetDayOfTheWeekData(DayOfWeekFiltersObject filterSearch,bool IsRo)
{
IsROCount = IsRo;
string RowCountQuery = string.Empty;
// Stopwatch sw = new Stopwatch();
//sw.Start();
try {
using (var con = Database.GetConnection(BMIS2DataBase))
{
con.Open();
using (var command = new SqlCommand())
{
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = IsRo? "[BMIS].[R_SP_GetRoCountQuery]" : "[BMIS].[R_SP_GetNoRoCountQuery]";
cmd.Parameters.Add(new SqlParameter("#clientid", filterSearch.ClientId));
cmd.Parameters.Add(new SqlParameter("#StartYear", DateTime.Parse(filterSearch.StartDate).ToString("yyyyMMdd")));
cmd.Parameters.Add(new SqlParameter("#EndYear", DateTime.Parse(filterSearch.EndDate).ToString("yyyyMMdd")));
cmd.Parameters.Add(new SqlParameter("#LocationId", filterSearch.SelectedLocationId));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#ToolType", filterSearch.ToolTypeName));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#TestType", filterSearch.TestType));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#VehicleStatus", filterSearch.VehicleStatusName==null?null:String.Join(",", filterSearch.VehicleStatusName)));
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
RowCountQuery = reader.GetString(reader.GetOrdinal("query"));
}
}
}
}
}
catch(Exception ex)
{
Log.Error(ex.Message);
}
//sw.Stop();
await this.ExecuteQuery(RowCountQuery, GetResultForRoCount);
return lstRoCont;
}
public async Task<bool> IsRowCount(DayOfWeekFiltersObject filterObj)
{
//HttpContext.Current.Session["ClientIdRoCount"] = ClientId;
string RowCountQuery = string.Empty;
using (var con = Database.GetConnection(BMIS2DataBase))
{
con.Open();
using (var command = new SqlCommand())
{
SqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "[BMIS].[R_SP_IsRoCountQuery]";
cmd.Parameters.Add(new SqlParameter("#clientid", filterObj.ClientId));
cmd.Parameters.Add(new SqlParameter("#StartYear", DateTime.Parse(filterObj.StartDate).ToString("yyyyMMdd")));
cmd.Parameters.Add(new SqlParameter("#EndYear", DateTime.Parse(filterObj.EndDate).ToString("yyyyMMdd")));
cmd.Parameters.Add(new SqlParameter("#LocationId", filterObj.SelectedLocationId));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#ToolType", filterObj.ToolTypeName));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#TestType", filterObj.TestType));
cmd.Parameters.Add(SetDbNull.SetDBNullIfEmpty("#VehicleStatus", filterObj.VehicleStatusName==null?null:String.Join(",",filterObj.VehicleStatusName)));
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
RowCountQuery = reader.GetString(reader.GetOrdinal("query"));
}
}
}
}
await this.ExecuteQuery(RowCountQuery, GetIsRoCount);
return IsROCount;
}
public override async Task ExecuteDaxReader(string query, Action MethodName)
{
DaxCommand.CommandText = query;
MethodName();
}
}
}
This is how there are 20 repositories that are implementing the same Abstract Dal.
I would be extremely thankful if anyone can help me resolve this issue.
Try testing your code by setting your TokenCache to null in the AuthenticationContext constructor and removing the ConfigureAwait(false). Without ConfigureAwait(false) it should deadlock immediately and with ConfigureAwait(false) it should work every time. Have you ensured that you are hitting that section?
Apparently this is an issue with later versions. One workaround is to downgrade to version 2.19.208020213
https://github.com/Azure/azure-sdk-for-net/issues/1432
I have gone through each Repository 1 by 1 and founded in few cases my colleagues has not put await to async functions which was causing deadlock. I have put this under monitoring and yes we have put ConfigureAwait(false).
I really appreciate your comment. Thanks

Confusion on getting access token from google api with mvc

I've been trying to follow a number of tutorials I can find to have an mvc application allow a user to authenticate the app and get the access and refresh tokens back. Unfortunately I can't find any that are clear enough to where I can follow what's going on. I started with google's sample code and then found some others like this one and this one.
When I run my app I'm trying to go to http://localhost:61581/Integration/Google/IndexAsync it hits that method which eventually hits the AppFlowMetadata.GetUserId method and then hits my custom TenixDataStore class' GetAsync method.
The things that are confusing are
First off, am I going to the right url/method? I think I am based on google's code example but not sure.
I thought that the key I would get would be the email address but instead is a GUID. Is that how google identifies a user?
If I'm going to the right url, why does the page just hang and never return. I expected it to open a google authorization page which didn't happen.
Here's my code.
AppFlowMetadata class
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Tenix.Domain.Constants;
namespace MyApp.Areas.Integration.Controllers
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = APIConstants.GMailApiKey,
ClientSecret = APIConstants.GmailApiSecret
},
Scopes = new[] {GmailService.Scope.GmailReadonly},
DataStore = new TenixDataStore()
});
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
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["UserID"];
if (user == null) return null;
return user.ToString();
}
}
}
GoogleController
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Gmail.v1;
using Google.Apis.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class GoogleController : Controller
{
public async Task IndexAsync(CancellationToken cancellationToken)
{
if (Session["UserID"] == null)
{
Response.Redirect("~/Login.aspx", true);
}
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 = "Tenix Gmail Integration"
});
}
}
}
}
TenixDataStore class
using System;
using System.Threading.Tasks;
using DataBaseUtilitiesTEN;
using Google.Apis.Json;
using Google.Apis.Util.Store;
using Newtonsoft.Json.Linq;
using Synergy.Extensions;
using Tenix.Domain.Data.Respositories;
using Tenix.Domain.Model.Integration;
using Tenix.Domain.Services;
namespace MyApp.Areas.Integration.Controllers
{
public class TenixDataStore : IDataStore
{
private readonly string conStr = ConnectionStrings.GeneralInfo;
private CredentialService _service;
public TenixDataStore()
{
_service = new CredentialService(new CredentialRepository(conStr));
}
public Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key MUST have a value");
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
var jObject = JObject.Parse(serialized);
var access_token = jObject.SelectToken("access_token");
var refresh_token = jObject.SelectToken("refresh_token");
if (access_token == null)
throw new ArgumentException("Missing access token");
if (refresh_token == null)
throw new ArgumentException("Missing refresh token");
_service.SaveUserCredentials(new UserCredential
{
EmailAddress = key,
AccessToken = (string)access_token,
RefreshToken = (string)refresh_token
});
return Task.Delay(0);
}
public Task DeleteAsync<T>(string key)
{
_service.DeleteCredentials(key);
return Task.Delay(0);
}
public Task<T> GetAsync<T>(string userId)
{
var credentials = _service.GetUserCredentials(userId.To<int>());
var completionSource = new TaskCompletionSource<T>();
if (!string.IsNullOrEmpty(credentials.AccessToken))
completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(credentials.AccessToken));
return completionSource.Task;
}
public Task ClearAsync()
{
return Task.Delay(0);
}
}
}
AuthCallbackController
using Google.Apis.Auth.OAuth2.Mvc;
namespace MyApp.Areas.Integration.Controllers
{
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData
{
get { return new AppFlowMetadata(); }
}
}
}
After spending days trying to figure this out and not making any headway with the google api .net libraries I ended up just going with my own implementation which after reading their documentation was at least something I could fully understand. In case anyone could use the code, here's what I ended up with. Still need to do some refactoring, but at this point it's working.
Just need to make sure the AuthorizeResponse and Authorize routes are registered as authorized redirect uris.
public class GoogleController : Controller
{
private readonly CredentialService _credentialService;
private readonly GoogleEndpoints _endpoints;
public GoogleController()
{
_endpoints = new GoogleEndpoints();
_credentialService = new CredentialService(new CredentialRepository(ConnectionStrings.GeneralInfo));
}
private string AuthorizeUrl
{
get
{
return "/Integration/Google/Authorize";
}
}
private string AuthorizeResponseUrl
{
get
{
return "/Integration/Google/AuthorizeResponse";
}
}
private string SaveResponseUrl
{
get
{
return "/Integration/Google/SaveResponse";
}
}
public void Authorize()
{
if (Session["UserID"] == null || Session["Email"] == null)
{
Response.Redirect("~/Login.aspx", true);
Session["LoginSource"] = AuthorizeUrl;
Response.End();
}
else
{
if (Session["SessionId"] == null || Session["SessionId"].ToString().Trim().Length == 0)
Session["SessionId"] = _credentialService.CreateSessionId(Session["UserID"].To<int>());
var url = _endpoints.AuthorizationEndpoint + "?" +
"client_id=" + APIConstants.GMailApiKey + "&" +
"response_type=code&" +
"scope=openid%20email&" +
"redirect_uri=" + AuthorizeResponseUrl + "&" +
"state=" + Session["SessionId"] + "&" +
"login_hint=" + Session["Email"] + "&" +
"access_type=offline";
Response.Redirect(url);
}
}
public ActionResult AuthorizeResponse()
{
var state = Request.QueryString["state"];
if (state == Session["SessionId"].ToString())
{
var code = Request.QueryString["code"];
var values = new Dictionary<string, object>
{
{"code", code},
{"redirect_uri", AuthorizeResponseUrl},
{"client_id", APIConstants.GMailApiKey},
{"client_secret", APIConstants.GmailApiSecret},
{"grant_type", "authorization_code"},
{"scope", ""}
};
var webmethods = new WebMethods();
var tokenResponse = webmethods.Post(_endpoints.TokenEndpoint, values);
var jobject = JObject.Parse(tokenResponse);
var access_token = jobject.SelectToken("access_token");
var refresh_token = jobject.SelectToken("refresh_token");
if (access_token == null || access_token.ToString().Trim().Length == 0)
{
//notify devs something went wrong
return View(new GoogleAuthResponse(tokenResponse, false));
}
var credentials = _credentialService.GetUserCredentials(Session["SessionId"].ToString());
credentials.AccessToken = access_token.ToString();
credentials.RefreshToken = refresh_token.ToString();
credentials.EmployeeId = Session["UserId"].To<int>();
_credentialService.SaveUserCredentials(credentials);
return View(new GoogleAuthResponse("Integration successful!", true));
}
return View(new GoogleAuthResponse("Missing state information.", false));
}
}
And the helper class to get the google endpoints.
public class GoogleEndpoints
{
public GoogleEndpoints()
{
using (var client = new WebClient())
{
var response = client.DownloadString("https://accounts.google.com/.well-known/openid-configuration");
var jobject = JObject.Parse(response);
AuthorizationEndpoint = jobject.SelectToken("authorization_endpoint").ToString();
TokenEndpoint = jobject.SelectToken("token_endpoint").ToString();
}
}
public string AuthorizationEndpoint { get; private set; }
public string TokenEndpoint { get; private set; }
}
The controller uses another couple of helper classes for parsing the json and posting the form data, but that should be pretty straightforward.

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:

Authentication in webAPI

I want to set Individual authentication for the web API application i have created in Visual studio 2013 using Asp.net . please tell me how can i do that .
VS 2013 by default provide several types of authentication while designing . i choose individual Authentication . But don't know how it works .
Create authentication token on server-side and store it in your database or even in cache. Then send this token with requests from your win forms application. WebApi should check this token all the time. It's good enough and you have full control over your auth process.
Basically it's similar to Darin's answer.
Let me share, how it works for me:
Object with Auth details:
public class TokenIdentity
{
public int UserID { get; set; }
public string AuthToken { get; set; }
public ISocialUser SocialUser { get; set; }
}
Web API Auth Controller:
public class AuthController : ApiController
{
public TokenIdentity Post(
SocialNetwork socialNetwork,
string socialUserID,
[FromUri]string socialAuthToken,
[FromUri]string deviceRegistrationID = null,
[FromUri]DeviceType? deviceType = null)
{
var socialManager = new SocialManager();
var user = socialManager.GetSocialUser(socialNetwork, socialUserID, socialAuthToken);
var tokenIdentity = new AuthCacheManager()
.Authenticate(
user,
deviceType,
deviceRegistrationID);
return tokenIdentity;
}
}
Auth Cache Manager:
public class AuthCacheManager : AuthManager
{
public override TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
if (HttpRuntime.Cache[authToken] != null)
{
return (TokenIdentity) HttpRuntime.Cache.Get(authToken);
}
return base.CurrentUser;
}
}
public int? CurrentUserID
{
get
{
if (CurrentUser != null)
{
return CurrentUser.UserID;
}
return null;
}
}
public override TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
if (socialUser == null) throw new ArgumentNullException("socialUser");
var identity = base.Authenticate(socialUser, deviceType, deviceRegistrationID);
HttpRuntime.Cache.Add(
identity.AuthToken,
identity,
null,
DateTime.Now.AddDays(7),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
return identity;
}
}
Auth Manager:
public abstract class AuthManager
{
public virtual TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserByToken(authToken);
if (user == null) return null;
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
public virtual TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserBySocialID(socialUser.SocialUserID, socialUser.SocialNetwork);
user = (user ?? new User()).CopyFrom(socialUser);
user.AuthToken = System.Guid.NewGuid().ToString();
if (user.ID == default(int))
{
usersRepo.Add(user);
}
usersRepo.SaveChanges();
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
Global Action Filter:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth"))
{
return;
}
var authManager = new AuthCacheManager();
var user = authManager.CurrentUser;
if (user == null)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
//Updates the authentication
authManager.Authenticate(user.SocialUser);
}
}
Global.asax registration:
GlobalConfiguration.Configuration.Filters.Add(new AuthFilterAttribute());
The idea is that AuthCacheManager extends AuthManager and decorates it's methods and properties. If there is nothing inside cache then go check database.
It is a little complicated! By default it is a Token-Based authenctication. Check these links for more details :
Individual Accounts in ASP.NET Web API: http://www.asp.net/vnext/overview/authentication/individual-accounts-in-aspnet-web-api
Understanding OWIN Forms authentication options : http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx#_Understanding_OWIN_Forms
also these links will help :
10 Things You Should Know about Tokens: http://blog.auth0.com/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies
Cookies vs Tokens. : http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/

ASP.NET MVC Store TempData in Cookie

Is there a way to let TempData store in a browser's cookie instead of Session State. I have Session State disabled on my site.
Thanks.
You can use brock Allen's Cookie TempData provider. Is fully documented here and it's also available as a NuGet package.
It takes into account, between other things, an important concern: security.
It's really easy to make MVC TempData use this package.
You can specify your own custom TempDataProvider and write it so that it stores in temp data.
Take a look at this blog post for an example where someone uses a custom tempdata provider
I use the following little class file:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web;
using System.Web.Mvc;
/* 16-09-2010
* pulled from Microsoft.Web.Mvc Futures
* be careful in case future versions of the mvc dll incorporate this
*
*/
namespace yournamespace
{
public class CookieTempDataProvider : ITempDataProvider
{
internal const string TempDataCookieKey = "__ControllerTempData";
readonly HttpContextBase _httpContext;
public CookieTempDataProvider(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
_httpContext = httpContext;
}
public HttpContextBase HttpContext
{
get
{
return _httpContext;
}
}
protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
{
IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value);
cookie.Expires = DateTime.MinValue;
cookie.Value = string.Empty;
if (_httpContext.Response != null && _httpContext.Response.Cookies != null)
{
HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey];
if (responseCookie != null)
{
cookie.Expires = DateTime.MinValue;
cookie.Value = string.Empty;
}
}
return deserializedTempData;
}
return new Dictionary<string, object>();
}
protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
var cookieValue = SerializeToBase64EncodedString(values);
var cookie = new HttpCookie(TempDataCookieKey)
{
HttpOnly = true, Value = cookieValue
};
_httpContext.Response.Cookies.Add(cookie);
}
public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)
{
var bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
var memStream = new MemoryStream(bytes);
var binFormatter = new BinaryFormatter();
return binFormatter.Deserialize(memStream, null) as IDictionary<string, object>;
}
public static string SerializeToBase64EncodedString(IDictionary<string, object> values)
{
var memStream = new MemoryStream();
memStream.Seek(0, SeekOrigin.Begin);
var binFormatter = new BinaryFormatter();
binFormatter.Serialize(memStream, values);
memStream.Seek(0, SeekOrigin.Begin);
var bytes = memStream.ToArray();
return Convert.ToBase64String(bytes);
}
IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext)
{
return LoadTempData(controllerContext);
}
void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
SaveTempData(controllerContext, values);
}
}
}
and then add it to my contoller as such:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
TempDataProvider = new CookieTempDataProvider(requestContext.HttpContext);
}
seems to work fine...
Nazaf,
try this for removing your cookies:
public void DeleteCookie(string name)
{
DateTime now = DateTime.UtcNow;
string cookieKey = name;
var cookie = new HttpCookie(cookieKey, null)
{
Expires = now.AddDays(-1)
};
HttpContext.Response.Cookies.Set(cookie);
}
usage:
DeleteCookie("__ControllerTempData");

Resources