External Login without using identity asp.net core 2.0 - asp.net-mvc

I'm trying to create an external login scheme for facebook, google and linkedin without using identity framework. I have an api that stores all users and do some authentication stuffs. Right now I'm kind of lost on how to get the information from the external login.
I'm issuing a challenge like this.
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider)
{
//Issue a challenge to external login middleware to trigger sign in process
return new ChallengeResult(provider);
}
This works well, it redirects me to either google, facebook or linkedinn authentication.
Now on this part:
public async Task<IActionResult> ExternalLoginCallback()
{
//Extract info from externa; login
return Redirect("/");
}
All I want is to get the information that was provided by the external login.
I have tried what I found from my research,
var result = await HttpContext.AuthenticateAsync(provider);
if (result?.Succeeded != true)
{
return Redirect("/");
}
var externalUser = result.Principal;
var claims = externalUser.Claims.ToList();
First of all I I'm not sure if a simple ?provider=Google on my callback string will pass the provider name I specify so it can be used to check the sign in scheme. I guess this is incorrect. Secondly, I tried hard coding await HttpContext.AuthenticateAsync("Google") and when it reach this code, the debug stops. I'm not sure why.
I've seen the generated code when creating a project with single authentication.
var info = await _signInManager.GetExternalLoginInfoAsync();
Sadly, I'm won't be able to use identity since I don't have a user store and my application will be consuming an API.

First you need to create a custom cookie handler. I myself had problems with:
No IAuthenticationSignInHandler is configured to handle sign in for
the scheme: Bearer
I had to add a cookie handler that will temporarily store the outcome of the external authentication, e.g. the claims that got sent by the external provider. This is necessary, since there are typically a couple of redirects involved until you are done with the external authentication process.
Startup
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
})
.AddCookie("YourCustomScheme")
.AddGoogle(googleOptions =>
{
googleOptions.SignInScheme = "YourCustomScheme";
googleOptions.ClientId = "x";//Configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = "x";//Configuration["Authentication:Google:ClientSecret"];
//googleOptions.CallbackPath = "/api/authentication/externalauthentication/signin-google";
});
The important part here is "YourCustomScheme".
Now it's time to retrieve the user information from the claims provided by the external authentication in the callback action.
Controller
[AllowAnonymous]
[HttpPost(nameof(ExternalLogin))]
public IActionResult ExternalLogin(ExternalLoginModel model)
{
if (model == null || !ModelState.IsValid)
{
return null;
}
var properties = new AuthenticationProperties { RedirectUri = _authenticationAppSettings.External.RedirectUri };
return Challenge(properties, model.Provider);
}
[AllowAnonymous]
[HttpGet(nameof(ExternalLoginCallback))]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
//Here we can retrieve the claims
var result = await HttpContext.AuthenticateAsync("YourCustomScheme");
return null;
}
VoilĂ ! We now have some user information to work with!
Helpful link
http://docs.identityserver.io/en/release/topics/signin_external_providers.html

I too had this issue and see if the below code works for you.
I wanted to extract the full name after Google/FB authentication.
var info = await _signInManager.GetExternalLoginInfoAsync();
TempData["fullname"] = info.Principal.FindFirstValue(ClaimTypes.Name);

Related

How to allow basic authentication on a single WebAPI endpoint in an MVC 5 app employing OpenID (ADFS)

I have a web application, hosted on our organization's web servers, which uses OpenID Connect (OIDC) in the shape of Active Directory Federated Services (ADFS) for authentication. It previously used forms authentication, but our organization is migrating all web apps to ADFS for improved security. The web app in general works as expected.
However, the app, written in MVC 5 targeting .NET Framework 4.7.2, also has a single WebAPI endpoint which is used for allowing users to subscribe, in Outlook, to an iCalendar feed exposed by the application (it's an appointment booking system). Before implementing ADFS, I made this work by using a custom AuthorizationFilterAttribute on the WebAPI endpoint which required username and password in the request header - basic authentication, in other words. It's far from ideal, but as far as I can tell it's the only kind of authentication Outlook supports.
Since implementing ADFS, though, the endpoint appears to be no longer accessible by Outlook (crucially, I'm not prompted for credentials when I add the calendar subscription). Adding it in the full Outlook app fails with no errors or feedback and doing so in the web client (an idea prompted by this article)
appears to work but again, I'm not prompted for credentials and no events ever appear in the calendar.
When I call it in a web browser I am redirected to the ADFS login page. When I call it in Postman with no Authorization header I similarly get the ADFS login page in response, but if I specify Basic Auth with the request and pass in my username and password, I get the correct response with the iCal feed.
As you can see below in my HandleUnathorized method I'm adding a WWW-Authenticate header which, as I understood it, was supposed to cause the client to prompt for login credentials when it received a 401 response, but this isn't happening.
My WebAPI controller looks like this:
namespace AppointmentBooking.Web.API.Controllers
{
public class AppointmentController : ApiController
{
[CustomBasicAuthentication] // Use a custom authentication filter for this method to support Outlook
[HttpGet]
[Route("ical/{username}")]
public HttpResponseMessage GetStaffICalendar(string username)
{
if (HttpContext.Current.User.Identity.IsAuthenticated == true)
{
string currentUsername = HttpContext.Current.User.Identity.Name;
if (username == currentUsername)
{
string calendarString = ICalendarService.GetICalendarForHost(currentUsername);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(calendarString, new UTF8Encoding(false), "text/calendar");
return response;
}
return Request.CreateResponse(HttpStatusCode.Unauthorized, "You may only open your own iCalendar, not that of another user");
}
return Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
My custom AuthorizationFilterAttribute is defined as follows:
namespace AppointmentBooking.Web.Api.Filters
{
public class CustomBasicAuthentication : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
var authenticationToken = actionContext.Request.Headers.Authorization.Parameter;
var decodedAuthenticationToken = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationToken));
var usernamePasswordArray = decodedAuthenticationToken.Split(':');
var userName = usernamePasswordArray[0];
var password = usernamePasswordArray[1];
var isValid = Membership.ValidateUser(userName, password);
if (isValid)
{
var principal = new GenericPrincipal(new GenericIdentity(userName), null);
actionContext.RequestContext.Principal = principal;
Thread.CurrentPrincipal = principal;
System.Web.HttpContext.Current.User = principal;
return;
}
}
HandleUnathorized(actionContext);
}
private static void HandleUnathorized(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", "Basic Scheme='Data' realm='Access your iCalendar");
}
}
}
And, in case it's helpful, the app's WebApiConfig.cs file is like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
}
How can I fix this endpoint so that it causes clients (particularly Outlook) to prompt for login credentials, rather than redirecting to the ADFS login page when I call it?

Owin.Providers yahoo FantasySports API Access Token

Very hard to understand how to use Oauth or OWIN for anything besides logging in. I have created an MVC web app. I can have users log into my app using their Yahoo ID (instead of a local ID) just fine. This is done using Owin.Security.Providers.Yahoo
I can also make API calls using DevDefinedOauth, here is a code snippet:
public ActionResult Test2(string oauth_token, string oauth_verifier)
{
if(String.IsNullOrEmpty(oauth_token))
{
oauthButton_Click();
}
else
{
OAuthSession session = (OAuthSession)Session["oAuthSession"];
IToken requestToken = (IToken)Session["oAuthToken"];
if (!String.IsNullOrEmpty(oauth_verifier))
{
IToken accessToken = session.ExchangeRequestTokenForAccessToken(requestToken, oauth_verifier);
Session["oAuthSession"] = session;
}
}
IConsumerRequest playerData = ((OAuthSession)Session["oAuthSession"]).Request().Get().ForUrl("http://fantasysports.yahooapis.com/fantasy/v2/game/371/players");
var xml = playerData.ToDocument();
My problem is I want to eliminate the use of DevDefinedOauth (if possible) and use only Owin.Security.Providers.Yahoo
I cannot find any documentation out there on how to use Owin to do this. Is it possible? I noticed I can try something like this, but it yields no results:
var token = new Owin.Security.Providers.Yahoo.Messages.AccessToken();
string test = token.Token;
I read a post on how to get the access token if you use facebook and Oauth, but the same code doesn't work for Yahoo. I am not sure if there is something that needs to be added to my Startup Class, or my Account controller action (below).
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
//var client = new Owin.Security.Providers.Yahoo.
return RedirectToLocal(returnUrl);
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
}
}
As you can see, if you look at my other posts, I am a little lost. I feel like I'm very close to the answer but just can't find it. Woirst case scenario I can use Owin Providers for logging in and then use DevDefinedOauth for API calls but it seems like a lot fo extra code (and usings) for nothing. If anyone has any ideas please let me know....

Authenticating users with auth token in query string with ASP.NET MVC

[This question relates to ASP.NET MVC4, and it is about best-practice approach - so please, don't suggest hacks.]
I want to authenticate users using an auth token sent in the request URL. It works similarly to a password reset token, except in this case it does not go to a reset page but instead grants access to some portion of the site. The idea is to send the URL with the auth token to a verified email address of the user. Users can click the link and perform some actions without typing their password.
Out-of-the-box, ASP.NET has the [Authorize] attribute and the SimpleMembershipProvider - these seem to work great, but they do some voodoo magic under the hood (like auto-generating database tables), so I don't know how to extend them to add this link-based auth token.
I don't expect an exact answer, but please do point me to the right direction.
Thanks!
Uf, broad question. But I will try at least to direct you to a right direction.
So first if suggest that you use Forms Authentication as a base, but you will have to customize using of it. And I presume that you do not want to use cookies for the authentication as this is native behaviour of the Forms Authentication.
The most important point you should consider to have it you custom query string token based authentication.
Create a login action and in this action you will authorize the user, if he have granted access you ask FormsAuthentication to create AuthCookie. For the further on you just take the httpCookie.Value as your auth token that you will carry in query string.
You need to implement the Application_BeginRequest in the Global.asax that will handle this query string tokens and translate it into the cookie. With this approach you can leverage all the ASP.NET Forms Authentication infrastructure.
This is quite high level picture w/o code. If you need more detail help I can also provide it to you.
You should just use a regular Action that accepts HttpGet.
Upon receiving the token, immediately invalid it so it can't be used again.
Also, only accept tokens that are within your pre-defined range of time period, like 24 or 72 hours.
Thank you Peter for idea.
If smb need to create JWT token authorization for old ASP.NET MVC5.I wrote small example. I don't serialize cookie to JWT. I create a JWT and after I am checking it in the BeginRequest. If everything is ok, I create a cookie and set it to the httpContext.Request. I used authentication mode="Forms" for application and it require cookies.
For create JWT token:
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
[AllowAnonymous]
[HttpPost]
public ActionResult LoginJWT(LoginViewModel model)
{
ActionResult response = null;
if (ModelState.IsValid)
{
if (true) //todo: check user login&password
{
var payload = new Dictionary<string, object>
{
{ "iss", "subject" },
{ "sub", "api" },
{ "exp", DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "jti", Guid.NewGuid() },
{ "uid", "64" } //custom field for identificate user
};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
response = Content(token);
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Login or password are not found");
}
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Errors in Model");
}
return response;
}
For check JWT token in Global.asax:
public override void Init()
{
this.BeginRequest += this.BeginRequestHandler;
base.Init();
}
private void BeginRequestHandler(object sender, EventArgs e)
{
var bearerToken = this.Context.Request.Headers["Authorization"];
if (bearerToken != null)
{
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
int userId = 0;
try
{
IJsonSerializer serializer = new JsonNetSerializer();
var provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.DecodeToObject<IDictionary<string, string>>(token, secret, verify: true);
if (json.TryGetValue("uid", out var uid))
{
userId = Convert.ToInt32(uid);
}
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
if (userId != 0)
{
// check user by id, if found create cookie.
}
}
}
I used:
jwt-dotnet/jwt library 7.2.1

Using OpenID/OpenAuth in MVC3 app with overridden authentication method

We override the basic authentication in an MVC3 application by calling a webservice with the user's credentials and returning a WCF structure that contains the user's ID, a "LogonTicket". This LogonTicket is used to "authenticate the user for each call made to the webservice.
Now, we override by replacing the defaultProvider in the Web.config. All we do in this overridden provider is
to override the ValidateUser() function. That is where we call the web service with their credentials and return
the "LogonTicket".
This is the LogOn() function from our AccountController, essentially the base code from the template:
public ActionResult LogOn(LogOnModel model)
{
string ReturnUrl = "";
if (HttpContext.Request.UrlReferrer.Query.Length > 11)
{
ReturnUrl = Uri.UnescapeDataString(HttpContext.Request.UrlReferrer.Query.Substring(11));
}
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
&& !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
{
return Redirect(ReturnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
ViewBag.MainWebsite = MainWebsite;
return View(model);
}
This is the overridden ValidateUser() function from our new default provider:
public override bool ValidateUser(string username, string password)
{
MyServiceClient mps = new MyServiceClient();
string sha1password = HashCode(password);
LogonInfo logonInfo = mps.GetLogonTicket(username, sha1password);
if (logonInfo.LogonTicket != "" && logonInfo.LogonTicket != "0")
{
// Authenticated so set session variables
HttpContext.Current.Session["LogonTicket"] = logonInfo.LogonTicket;
HttpContext.Current.Session["ParticipantID"] = logonInfo.ParticipantID;
return true;
}
else
{
return false;
}
}
I'm not really sure how to combine the use of the two, so my questions are:
How can I implement OpenID and Facebook logins and keep my current authentication method?
How can we "map" the OpenID user with our current user DB values? We MUST know so we can retrieve their info.
I know we can retrieve their email address but what if their OpenID email is different than the one they use for their record on our site?
Are there any examples of how to do this, anywhere?
Thanks for looking at my question.
I have done a project which required multiple log-on possibilities (custom account, Google and Facebook)
In the end your authentication with ASP.NET is entirely dependant on your configuration. (In your case it is FormsAuthentication) this means that FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); basicly determines everything in regard to your user and where you set this isn't restricted.
You have now basicly the same implementation as we started out with, using a MembershipProvider to handle your own custom account. You only need to expand now to facilitate the openIds. You would have to expand your Controller with various actions for each login type (Now you have ActionResult LogOn() you can add to that for example: ActionResult LogOnOpenId()). Inside that method you basicly call the same code but instead of Membership.ValidateUser(model.UserName, model.Password) you call the OpenId services.
I have provided below an example of our google implementation using dotnetopenauth. The service method uses formsService.SignIn(userId.Value.ToString(), false); which basicly calls FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); (we only do some custom behaviour there in regard to the SecurityPrincipal but this doesn't affect your Authentication process). You can also see that we make a new account when we receive a new user. To solve your question part 2 we have implemented a profile which can be merged if you can provide another login. This allows our users to keep their account consolidated and use whatever login method they like.
For examples in regard to multiple signons I will refer to the answer of Tomas whom referenced StackExchange as a good example. Also I'd advise you to install MVC4 and VS2012 and just do a File > New Project. The newest default template of MVC includes openid implementation alongside a custom login!
Example google openid implementation:
The controller method:
public virtual ActionResult LoginGoogle(string returnUrl, string runAction)
{
using (var openId = new OpenIdRelyingParty())
{
IAuthenticationResponse response = openId.GetResponse();
// If we have no response, start
if (response == null)
{
// Create a request and redirect the user
IAuthenticationRequest req = openId.CreateRequest(WellKnownProviders.Google);
var fetch = new FetchRequest();
fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);
fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
fetch.Attributes.AddRequired(WellKnownAttributes.Preferences.Language);
req.AddExtension(fetch);
req.RedirectToProvider();
return null;
}
_service.ConnectViaGoogle(response, TempData);
}
The service method:
public void ConnectViaGoogle(IAuthenticationResponse response, TempDataDictionary tempData)
{
// We got a response - check it's valid and that it's me
if (response.Status == AuthenticationStatus.Authenticated)
{
var claim = response.GetExtension<FetchResponse>();
Identifier googleUserId = response.ClaimedIdentifier;
string email = string.Empty;
string firstName = string.Empty;
string lastName = string.Empty;
string language = string.Empty;
if (claim != null)
{
email = claim.GetAttributeValue(WellKnownAttributes.Contact.Email);
firstName = claim.GetAttributeValue(WellKnownAttributes.Name.First);
lastName = claim.GetAttributeValue(WellKnownAttributes.Name.Last);
language = claim.GetAttributeValue(WellKnownAttributes.Preferences.Language);
}
//Search User with google UserId
int? userId = _userBL.GetUserIdByGoogleSingleSignOnId(googleUserId);
//if not exists -> Create
if (!userId.HasValue)
{
_userBL.CreateGoogleUser(
googleUserId,
firstName,
lastName,
email,
language,
DBConstants.UserStatus.DefaultStatusId,
out userId);
}
if (userId.HasValue)
{
_userBL.UpdateLastLogon(userId.Value);
var formsService = new FormsAuthenticationService();
formsService.SignIn(userId.Value.ToString(), false);
AfterLoginActions(tempData);
}
}
}
Any questions or comments? I'll gladly hear them.
it should be perfectly possible to have multiple authentications methods. All IIS / ASP.net cares about is the FormsAuthentication cookies. So you would have one set of actions for your standard username/password auth, and another for OpenId. This is at least what I have done on one project.
You can't even trust the openId provider to give you an email address! A common solution to this problem is to allow a user to attach multiple OpenId identifiers (URI's) to the his account after logging in. This is e.g. how StackOverflow works. If this is the first time the user visits the system then you can auto create a new account, or force the user through a signup process.
When I added the OpenId support in the system mentioned, it had an existing table used to store username and password(users table). I added a new table with a many to one relationship with the users table, and used this to store the URI's.
As mentioned above StackOverflow it self is a good place to start, also there are a lot of good examples in the http://www.dotnetopenauth.net/ project.
As far as I know the source of SO is not public, and they are using the dotnetopenauth project.
This may be to abstract, but this library is a openId (among other things) for the open source orchard CMS: http://orchardopenauth.codeplex.com/
I hope this helps, but if you have any questions then please expand your question with more details.

Serving an iCalendar file in ASPNET MVC with authentication

I'm trying to serve an iCalendar file (.ics) in my MVC application.
So far it's working fine. I have an iPhone subscribing to the URL for the calendar but now I need to serve a personalised calendar to each user.
When subscribing to the calendar on the iPhone I can enter a username and password, but I don't know how to access these in my MVC app.
Where can I find details of how the authentication works, and how to implement it?
It turns out that Basic Authentication is what is required. I half had it working but my IIS configuration got in the way. So, simply returning a 401 response when there is no Authorization header causes the client (e.g. iPhone) to require a username/password to subscribe to the calendar.
On the authorization of the request where there is an Authorization request header, the basic authentication can be processed, retrieving the username and password from the base 64 encoded string.
Here's some useful code for MVC:
public class BasicAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var auth = filterContext.HttpContext.Request.Headers["Authorization"];
if (!String.IsNullOrEmpty(auth))
{
var encodedDataAsBytes = Convert.FromBase64String(auth.Replace("Basic ", ""));
var value = Encoding.ASCII.GetString(encodedDataAsBytes);
var username = value.Substring(0, value.IndexOf(':'));
var password = value.Substring(value.IndexOf(':') + 1);
if (MembershipService.ValidateUser(username, password))
{
filterContext.HttpContext.User = new GenericPrincipal(new GenericIdentity(username), null);
}
else
{
filterContext.Result = new HttpStatusCodeResult(401);
}
}
else
{
if (AuthorizeCore(filterContext.HttpContext))
{
var cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null);
}
else
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusDescription = "Unauthorized";
filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", "Basic realm=\"Secure Calendar\"");
filterContext.HttpContext.Response.Write("401, please authenticate");
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.Result = new EmptyResult();
filterContext.HttpContext.Response.End();
}
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
Then, my controller action looks like this:
[BasicAuthorize]
public ActionResult Calendar()
{
var userName = HttpContext.User.Identity.Name;
var appointments = GetAppointments(userName);
return new CalendarResult(appointments, "Appointments.ics");
}
I found this really helpful, but i hit a few problems during the development and i thought i would share some of them to help save other people some time.
I was looking to get data from my web application into the calendar for an android device and i was using discountasp as a hosting service.
The first problem i hit was that the validation did not work when uploaded to the server, stangely enough it was accepting my control panel login for discountasp but not my forms login.
The answer to this was to turn off Basic Authentication in IIS manager. This resolved the issue.
Secondly, the app i used to sync the calendar to the android device was called iCalSync2 - its a nice app and works well. But i found that it only worked properly when the file was delivered as a .ics (duh for some reason i put it as a .ical.. it must have been late) and i also had to choose the webcal option
Lastly i found i had to add webcal:// to the start of my url instead of http://
Also be careful as the code posted above ignores the roles input variable and always passes nothing so you might need to do some role based checks inside your calendar routine or modify the code above to process the roles variable.

Resources