I am new with authorization and security to applications. I'm building upon my angularjs and web api app that uses Owin and AspNet.Identity.EntityFramework. I've been able to get the authorization working to force a user to either register / log in to the app. Now I'm looking on how to add more specific access such as a an admin role or specific user to look at more sensitive data. I've started with the [Authorize] attribute. Which forced the security. Then I added [Authorize(User="tbryant")] which didnt allow other users and even the user tbryant to log in. There is a user name in the AspNetUsers table of tbryant.
Here is my sample data from my api controller:
[RoutePrefix("api/Orders")]
public class OrdersController : ApiController
{
[Authorize(Users="tbryant")]
[Route("")]
public IHttpActionResult Get()
{
return Ok(Order.CreateOrders());
}
}
public class Order
{
public int OrderID { get; set; }
public string CustomerName { get; set; }
public string ShipperCity { get; set; }
public Boolean IsShipped { get; set; }
public static List<Order> CreateOrders()
{
List<Order> OrderList = new List<Order>
{
new Order {OrderID = 10248, CustomerName = "Tee Joudeh", ShipperCity = "Cleveland", IsShipped = true },
new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Columbus", IsShipped = false},
new Order {OrderID = 10250,CustomerName = "Thomas Yaser", ShipperCity = "Detroit", IsShipped = false },
new Order {OrderID = 10251,CustomerName = "Lena Jones", ShipperCity = "Ann Arbor", IsShipped = false},
new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Bamberg", IsShipped = true}
};
return OrderList;
}
}
Make sure the "user.Identity.Name" in HttpContext is equal to "tbryant".
Here is how Authorize attribute works.
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
return true;
}
Related
I have added a WebAuthenticator.AuthenticateAsync method in my xamarin forms app with start up Url as "https://accounts.google.com/o/oauth2/auth"
and call back url as "myapp://"
I have also tried with call back url as "com.googleusercontent.apps.{clientId}:/oauth2redirect"
I am doing this to add google login in my xamarin forms app.
On this browser with available google accounts are been showing up and after successful completion of email authentication it returns to app but result is not returned from WebAuthenticator.AuthenticateAsync method.
On second time invocation of this method returns the first invocation result as cancelled by user and the browser opens again for second time email authentication.
But it works in ios.
I have added 3 classes
public class Auth0Client
{
private readonly OidcClient oidcClient;
public Auth0Client(Auth0ClientOptions options)
{
var discovery = new DiscoveryPolicy
{
ValidateEndpoints = false,
Authority = "https://accounts.google.com"
};
oidcClient = new OidcClient(new OidcClientOptions
{
Authority = $"https://accounts.google.com/o/oauth2/auth",
ClientId = options.ClientId,
Scope = options.Scope,
RedirectUri = options.RedirectUri,
Browser = options.Browser,
ProviderInformation = options.ProviderInformation,
Policy = new Policy
{
Discovery = discovery,
RequireAccessTokenHash = false
},
});
}
public IdentityModel.OidcClient.Browser.IBrowser Browser
{
get
{
return oidcClient.Options.Browser;
}
set
{
oidcClient.Options.Browser = value;
}
}
public async Task<LoginResult> LoginAsync()
{
return await oidcClient.LoginAsync();
}
}
public class Auth0ClientOptions
{
public Auth0ClientOptions()
{
}
public string Domain { get; set; }
public string ClientId { get; set; }
public string RedirectUri { get; set; }
public string Scope { get; set; }
public IBrowser Browser { get; set; }
public ProviderInformation ProviderInformation { get; set; }
}
public class WebBrowserAuthenticator : IBrowser
{
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
try
{
WebAuthenticatorResult result = await WebAuthenticator.AuthenticateAsync(
new Uri(options.StartUrl),new Uri(options.EndUrl));
var url = new RequestUrl(options.EndUrl)
.Create(new Parameters(result.Properties));
return new BrowserResult
{
Response = url,
ResultType = BrowserResultType.Success
};
}
catch (Exception ex)
{
return new BrowserResult
{
ResultType = BrowserResultType.UserCancel,
ErrorDescription = "Login canceled by the user."
};
}
}
}
In view model we are creating authoclient
private async void NavigateToGoogleLogin(object obj)
{
string clientId = null;
string redirectUri = null;
switch (Device.RuntimePlatform)
{
case Device.iOS:
clientId = AppConstants.GoogleiOSClientId;
redirectUri = AppConstants.GoogleiOSRedirectUrl;
break;
case Device.Android:
clientId = AppConstants.GoogleAndroidClientId;
redirectUri = AppConstants.GoogleAndroidRedirectUrl;
break;
}
var auth0client = new Auth0Client(new Auth0ClientOptions()
{
Domain = "accounts.google.com/o/oauth2/auth",
ClientId = clientId,
RedirectUri = redirectUri,
Scope = AppConstants.GoogleScope,
Browser = new WebBrowserAuthenticator(),
ProviderInformation = new ProviderInformation
{
IssuerName = "accounts.google.com",
AuthorizeEndpoint = AppConstants.GoogleAuthorizeUrl,
TokenEndpoint = AppConstants.GoogleAccessTokenUrl,
UserInfoEndpoint = AppConstants.GoogleUserInfoUrl,
KeySet = new JsonWebKeySet(),
},
});
var loginResult = await auth0client.LoginAsync();
}
We are using below constants in authoclient object creation
internal static string GoogleScope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
internal static string GoogleAuthorizeUrl = "https://accounts.google.com/o/oauth2/auth";
internal static string GoogleAccessTokenUrl = "https://www.googleapis.com/oauth2/v4/token";
internal static string GoogleUserInfoUrl = "https://www.googleapis.com/oauth2/v3/userinfo";
Xamarin forms version:5.0.0.2012
Xamarin essentials: 1.7.3
Thanks in advance
I'm working on a MVC project. I want to use custom authorization attribute. First of all I used an example in this blog post.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string RolesConfigKey { get; set; }
protected virtual CustomPrincipal CurrentUser => HttpContext.Current.User as CustomPrincipal;
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAuthenticated) return;
var authorizedRoles = ConfigurationManager.AppSettings["RolesConfigKey"];
Roles = string.IsNullOrEmpty(Roles) ? authorizedRoles : Roles;
if (string.IsNullOrEmpty(Roles)) return;
if (CurrentUser == null) return;
if (!CurrentUser.IsInRole(Roles)) base.OnAuthorization(filterContext);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAuthenticated) return;
}
}
I use this custom principal in my base controller.
public class CustomPrincipal : IPrincipal
{
public CustomPrincipal(string userName) { this.Identity = new GenericIdentity(userName); }
public bool IsInRole(string userRoles)
{
var result = true;
var userRolesArr = userRoles.Split(',');
foreach (var r in Roles)
{
if (userRolesArr.Contains(r)) continue;
result = false;
break;
}
return result;
}
public IIdentity Identity { get; }
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] Roles { get; set; }
}
In my routeconfig my default route is /Account/Index where users login operations in. And this is account controllers Index action.
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Index(AccountViewModel accountModel)
{
var returnUrl = string.Empty;
if (!ModelState.IsValid) { return UnsuccessfulLoginResult(accountModel.UserName, ErrorMessages.WrongAccountInfo); }
var account = _accountService.CheckUser(accountModel.UserName, accountModel.Password);
if (account == null) return UnsuccessfulLoginResult(accountModel.UserName, ErrorMessages.WrongAccountInfo);
var roles = account.Roles.Select(r => r.RoleName).ToArray();
var principalModel = new CustomPrincipalModel
{
UserId = account.UserId,
FirstName = "FirstName",
LastName = "LastName",
Roles = roles
};
var userData = JsonConvert.SerializeObject(principalModel);
var ticket = new FormsAuthenticationTicket(1, account.UserId, DateTime.Now, DateTime.Now.AddMinutes(30), false, userData);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
Response.Cookies.Add(cookie);
SetCulture(account.DefaultCulture);
if (!Array.Exists(roles, role => role == "admin" || role == "user")) return UnsuccessfulLoginResult(accountModel.UserName, ErrorMessages.WrongAccountInfo);
if (roles.Contains("admin")) { returnUrl = Url.Action("Index", "Admin"); }
if (roles.Contains("user")) { returnUrl = Url.Action("Index", "Upload"); }
return SuccessfulLoginResult(accountModel.UserName, returnUrl);
}
As you can see when user is in admin role this action redirects user /Admin/Index otherwise /Upload/Index. But after I logged in a user has user role and typed /Admin/Index , authorization filters not working and user can access admin page.
Although I have added to UploadController and AdminController this attribute this error is occuring. How can I fix this ?
[CustomAuthorize(Roles = "user")]
public class UploadController : BaseController
[CustomAuthorize(Roles = "admin")]
public class AdminController : BaseController
You need to add claims for your user, add this part to your method:
. . .
var roles = account.Roles.Select(r => r.RoleName).ToArray();
ClaimsIdentity identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountModel.UserName));
roles.ToList().ForEach((role) => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
identity.AddClaim(new Claim(ClaimTypes.Name, userCode.ToString()));
. . .
Problem solved with these changes.
In my CustomAuthorizeAttribute changed this line
if (!filterContext.HttpContext.Request.IsAuthenticated) return;
to
if (!filterContext.HttpContext.Request.IsAuthenticated) base.OnAuthorization(filterContext);
And removed lines that I read allowed roles from web config. So my attributes final version like below
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected virtual CustomPrincipal CurrentUser => HttpContext.Current.User as CustomPrincipal;
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAuthenticated) base.OnAuthorization(filterContext);
if (string.IsNullOrEmpty(Roles)) return;
if (CurrentUser == null) return;
if (!CurrentUser.IsInRole(Roles)) filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
Then I added a controller named ErrorController and redirected to this page when user not in role.
With these changes I realized that I was unable to access my /Account/Index and added [AllowAnonymous] attribute to actions below.
[AllowAnonymous]
public ActionResult Index() { return View(); }
[HttpPost, ValidateAntiForgeryToken, AllowAnonymous]
public ActionResult Index(AccountViewModel accountModel)
I'm using Asp.net Identity Framework 2.1. I implement customized ApplicatoinUser, ApplicationRole, ApplicationUserRole, because I want to add support to multi-tenant, that is each user belongs to different companies, but I have 3 roles among all these companies, they are User, Admin and Approver.
My ApplicationUserRole derived from IdentityUserRole, and have one more property: CompanyId. This property will indicate the user's role in this particular company. My code for these customized classes attached in bottom.
My question is when I try to override ApplicationUserManager(Yes, it derived from UserManager too)'s AddToRoleAsync , IsInRoleAsync , I don't know how to deal with the new CompanyId, looks like the existing function doesn't receive these companyId(or tenantId).
Then when I'm trying to overload these functions with companyId included, I can't find the db context either in ApplicatoinUserManager nor its base class.
Am I on the right track of adding tenantId/companyId to the application Role?
I've referenced this answer: SO linkes, and this blog.ASP.NET Web Api and Identity 2.0 - Customizing Identity Models and Implementing Role-Based Authorization
My IdentityModels:
public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string>
{
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public string CompanyId { get; set; }
}
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser<string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>//, IAppUser
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
}
public virtual string CompanyId { get; set; }
public virtual List<CompanyEntity> Company { get; set; }
public DateTime CreatedOn { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
}
// Must be expressed in terms of our custom UserRole:
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
public ApplicationRole() {}
public ApplicationRole(string name) : this()
{
this.Name = name;
}
// Add any custom Role properties/code here
public string Description { get; set; }
}
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore: UserStore<ApplicationUser, ApplicationRole, string,ApplicationUserLogin, ApplicationUserRole,ApplicationUserClaim>, IUserStore<ApplicationUser, string>, IDisposable
{
public ApplicationUserStore()
: this(new IdentityDbContext())
{
base.DisposeContext = true;
}
public ApplicationUserStore(DbContext context)
: base(context)
{
}
}
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
public ApplicationRoleStore()
: base(new IdentityDbContext())
{
base.DisposeContext = true;
}
public ApplicationRoleStore(DbContext context)
: base(context)
{
}
}
My IdentityConfig:
public class ApplicationUserManager
: UserManager<ApplicationUser, string>
{
public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
: base(store) { }
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager = new ApplicationUserManager(
new UserStore<ApplicationUser, ApplicationRole, string,
ApplicationUserLogin, ApplicationUserRole,
ApplicationUserClaim>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
//RequireNonLetterOrDigit = true,
//RequireDigit = true,
//RequireLowercase = true,
//RequireUppercase = true,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
// add sms and email service provider
manager.SmsService = new EMaySmsServiceProvider();
manager.EmailService = new ConcordyaEmailServiceProvider();
return manager;
}
public string GetCurrentCompanyId(string userName)
{
var user = this.FindByName(userName);
if (user == null)
return string.Empty;
var currentCompany = string.Empty;
if (user.Claims.Count > 0)
{
currentCompany = user.Claims.Where(c => c.ClaimType == ConcordyaPayee.Core.Common.ConcordyaClaimTypes.CurrentCompanyId).FirstOrDefault().ClaimValue;
}
else
{
currentCompany = user.CurrentCompanyId;
}
return currentCompany;
}
public override Task<IdentityResult> AddToRoleAsync(string userId, string role, string companyId)
{
return base.AddToRoleAsync(userId, role);
}
#region overrides for unit tests
public override Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
{
return base.CheckPasswordAsync(user, password);
}
public override Task<ApplicationUser> FindByNameAsync(string userName)
{
return base.FindByNameAsync(userName);
}
#endregion
}
public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
public ApplicationRoleManager(IRoleStore<ApplicationRole, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
return new ApplicationRoleManager(
new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
}
}
First of all, I would like to say thanks for taking it this far. It gave me a great start for my multi-tenant roles solution. I'm not sure if I'm 100% right, but this works for me.
Firstly, you cannot override any of the "RoleAsync" methods, but you can overload them. Secondly, the UserStore has a property called "Context" which can be set to your DbContext.
I had to overload the "RoleAsyc" methods in both my UserStore and UserManager extended classes. Here is an example from each to get you going:
MyUserStore
public class MyUserStore : UserStore<MyUser, MyRole, String, IdentityUserLogin, MyUserRole, IdentityUserClaim> {
public MyUserStore(MyDbContext dbContext) : base(dbContext) { }
public Task AddToRoleAsync(MyUser user, MyCompany company, String roleName) {
MyRole role = null;
try
{
role = Context.Set<MyRole>().Where(mr => mr.Name == roleName).Single();
}
catch (Exception ex)
{
throw ex;
}
Context.Set<MyUserRole>().Add(new MyUserRole {
Company = company,
RoleId = role.Id,
UserId = user.Id
});
return Context.SaveChangesAsync();
}
}
MyUserManager
public class MyUserManager : UserManager<MyUser, String>
{
private MyUserStore _store = null;
public MyUserManager(MyUserStore store) : base(store)
{
_store = store;
}
public Task<IList<String>> GetRolesAsync(String userId, int companyId)
{
MyUser user = _store.Context.Set<MyUser>().Find(new object[] { userId });
MyCompany company = _store.Context.Set<MyCompany>().Find(new object[] { companyId });
if (null == user)
{
throw new Exception("User not found");
}
if (null == company)
{
throw new Exception("Company not found");
}
return _store.GetRolesAsync(user, company);
}
}
From here a couple scary things happen and I don't know a better way to manage them.
The User "IsInRole" method in the HttpContext will work but it will not be tenant-sensitive so you can no longer use it.
If you use the "Authorize" attribute, the same idea for "scary thing 1" applies, but here you can just extend it and make things happy for your system. Example below:
MyAuthorizeAttribute
public class MyAuthorizeAttribute : AuthorizeAttribute {
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (null == httpContext)
{
throw new ArgumentNullException("httpContext");
}
HttpSessionStateBase session = httpContext.Session;
IList<String> authorizedRoleNames = Roles.Split(',').Select(r => r.Trim()).ToList();
if (!httpContext.User.Identity.IsAuthenticated)
{
return false;
}
if (null == session["MyAuthorize.CachedUsername"])
{
session["MyAuthorize.CachedUsername"] = String.Empty;
}
if (null == session["MyAuthorize.CachedCompanyId"])
{
session["MyAuthorize.CachedCompanyId"] = -1;
}
if (null == session["MyAuthorize.CachedUserCompanyRoleNames"])
{
session["MyAuthorize.CachedUserCompanyRoleNames"] = new List<String>();
}
String cachedUsername = session["MyAuthorize.CachedUsername"].ToString();
int cachedCompanyId = (int)session["MyAuthorize.CachedCompanyId"];
IList<String> cachedUserAllRoleNames = (IList<String>)session["MyAuthorize.CachedUserAllRoleNames"];
IPrincipal currentUser = httpContext.User;
String currentUserName = currentUser.Identity.Name;
int currentCompanyId = (int)session["CurrentCompanyId"];//Get this your own way! I used the Session in the HttpContext.
using (MyDbContext db = MyDbContext.Create())
{
try
{
MyUser mUser = null;
ICollection<String> tmpRoleIds = new List<String>();
if (cachedUsername != currentUserName)
{
session["MyAuthorize.CachedUsername"] = cachedUsername = String.Empty;
//Reload everything
mUser = db.Users.Where(u => u.Username == currentUserName).Single();
session["MyAuthorize.CachedUsername"] = currentUserName;
session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = -1; //Force Company Reload
cachedUserCompanyRoleNames.Clear();
}
if (cachedUserCompanyRoleNames.Count != db.Users.Where(u => u.Username == currentUserName).Single().Roles.Select(r => r.RoleId).ToList().Count)
{
cachedUserCompanyRoleNames.Clear();
if (0 < currentCompanyId)
{
if(null == mUser)
{
mUser = db.Users.Where(u => u.Username == cachedUsername).Single();
}
tmpRoleIds = mUser.Roles.Where(r => r.Company.Id == currentCompanyId).Select(r => r.RoleId).ToList();
session["MyAuthorize.CachedUserCompanyRoleNames"] = cachedUserCompanyRoleNames = db.Roles.Where(r => tmpRoleIds.Contains(r.Id)).Select(r => r.Name).ToList();
session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = currentCompanyId;
}
}
if (cachedCompanyId != currentCompanyId)
{
cachedUserCompanyRoleNames.Clear();
//Reload company roles
if (0 < currentCompanyId)
{
if(null == mUser)
{
mUser = db.Users.Where(u => u.Username == cachedUsername).Single();
}
tmpRoleIds = mUser.Roles.Where(r => r.Company.Id == currentCompanyId).Select(r => r.RoleId).ToList();
session["MyAuthorize.CachedUserCompanyRoleNames"] = cachedUserCompanyRoleNames = db.Roles.Where(r => tmpRoleIds.Contains(r.Id)).Select(r => r.Name).ToList();
session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = currentCompanyId;
}
}
}
catch (Exception ex)
{
return false;
}
}
if (0 >= authorizedRoleNames.Count)
{
return true;
}
else
{
return cachedUserCompanyRoleNames.Intersect(authorizedRoleNames).Any();
}
}
}
In closing, as I said, I'm not sure if this is the best way to do it, but it works for me. Now, throughout your system, make sure you used your overloaded methods when dealing with Roles. I am also thinking about caching the Roles in a MVC BaseController that I wrote so that I can get similar functionality to User.IsInRole in all of my MVC Views.
I am using MVC and WebApi in same project, not using Session in WebApi.
In MVC BaseController I have something along these line
protected const string HOME = "HOME";
protected SessionDataStore SessionDataStore
{
get
{
if ((Session[HOME] != null) && (Session[HOME] is SessionDataStore))
{
return (SessionDataStore)Session[HOME];
}
}
set
{
Session[HOME] = value;
}
}
public class SessionDataStore
{
public Request CurrentRequest { get; set; }
public Customer CurrentCustomer { get; set; }
public List<Salutation> Salutations { get; set; }
public WebServiceConfiguration CurrentConfiguration { get; set; }
public List<int> CurrentConfigurationIds { get; set; }
public List<int> CurrentAttributeIds { get; set; }
public SessionDataStore()
{
CurrentRequest = new Request();
CurrentCustomer = new Customer();
Salutations = new List<Salutation>()
{
new Salutation() { Id = 1, Name = "Mr", },
new Salutation() { Id = 2, Name = "Mrs", },
new Salutation() { Id = 3, Name = "Miss", },
new Salutation() { Id = 4, Name = "Ms", },
new Salutation() { Id = 5, Name = "Dr", },
new Salutation() { Id = 6, Name = "Prof", },
new Salutation() { Id = 7, Name = "Other", }
};
CurrentConfiguration = new WebServiceConfiguration();
CurrentConfigurationIds= new List<int>();
CurrentAttributeIds = new List<int>();
}
}
In my MVC project, I call the web API and update the Session object like this.
I initialise SessionDataStore in the index page to a new object
var client = new HttpClient(_handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(string.Concat(GetBaseUrl().AbsoluteUri + "api/request/", requestId)).Result;
SessionDataStore.CurrentRequest = response.IsSuccessStatusCode ? response.Content.ReadAsAsync<Request>().Result : null;
The status code is Success but then I am losing the session.
Not sure why this is happening. I am using inProc session
Web API is REST-compliant, and REST is stateless. In other words, no session. You cannot access the session in a Web API action. If you need to interact with the session via an AJAX call, you will need to simply create an MVC Controller action to handle it instead of a Web API ApiController action.
If you didn't find the answer yet, this is how you can enable session usage inside web api request. You have to write the Application_PostAuthorizeRequest inside the Global.asax.cs like this:
protected void Application_PostAuthorizeRequest()
{
if(IsWebApiRequest())
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Contains("/api/");
}
This is my first time creating a web service. I am not sure if my implementation is incorrect, but I am trying to use much like a class. The problem is that when I am trying to consume I am getting confused and not being able to set the values of the properties.
here is the web service.
public class Service1 : System.Web.Services.WebService
{
private bool _isUserActive { get; set; }
private bool _isCredentialValid { get; set; }
public string email { get; set; }
public string pass { get; set; }
public int customerID { get; set; }
[WebMethod]
public bool VerifyUserCredential()
{
bool result = false;
PURLDataContext purl = new PURLDataContext();
try
{
var res = purl.Sel_User(email.ToLower(), pass);
if (res != null)
result = true;
_isUserActive = true;
_isCredentialValid = true;
}
catch (Exception ex)
{
if (ex.Message == "Account is inactive, please contact your administrator!")
{
_isUserActive = false;
_isCredentialValid = false;
}
else
_isCredentialValid = false;
//Invalid credentials.
}
return result;
}
[WebMethod]
public ArrayList retrieveCustomerInfo()
{
ArrayList customerInfo = new ArrayList();
string validate = "Please Validate";
if (_isCredentialValid)
{
PURLDataContext purl = new PURLDataContext();
var customer = purl.Sel_Recipient(customerID);
foreach (var c in customer)
{
customerInfo.Add(c);
}
}
else
customerInfo.Add(validate);
return customerInfo;
}
}
Here is what I am trying to do to consume.
PURLServices.Service1SoapClient webserv = new Service1SoapClient();
bool result;
ArrayOfAnyType array = new ArrayOfAnyType();
webserv.email = "email#email.com";
webserv.pass = "pass";
webserv.customerID = 12345;
result = webserv.VerifyUserCredential();
array = webserv.retrieveCustomerInfo();
Thank you for any help/
You do not want to try to use properties like this. Your method should look more like this:
public bool VerifyUserCredential(string userName, string password)
{
// method body here
}
Probably you would want to return an access token of some sort that the server will cache. This can then be passed into other methods to show that the user is valid.