I'm trying to store a user token once my application has been authorized with Dropbox so that when I open a different form (which will have drag and drop functionality), the user won't have to authorize the app again and will be able to perform upload functions to Dropbox.
My class for Authorizing Dropbox with DropNet is:
I have declared two properties; public string UserToken { get; set; } and public string UserSecret { get; set; }
string appKey = "APP_KEY";
string appSecret = "APP_SECRET";
public bool LinkDrpbox()
{
bool dropboxLink = false;
Authenticate(
url =>
{
var proc = Process.Start("iexplore.exe", url);
proc.WaitForExit();
Authenticated(
() =>
{
dropboxLink = true;
},
exc => ShowException(exc));
},
ex => dropboxLink = false);
if (dropboxLink)
{
return true;
}
else
{
return false;
}
}
private DropNetClient _Client;
public DropNetClient Client
{
get
{
if (_Client == null)
{
_Client = new DropNetClient(appKey, appSecret);
if (IsAuthenticated)
{
_Client.UserLogin = new UserLogin
{
Token = UserToken,
Secret = UserSecret
};
}
_Client.UseSandbox = true;
}
return _Client;
}
}
public bool IsAuthenticated
{
get
{
return UserToken != null &&
UserSecret != null;
}
}
public void Authenticate(Action<string> success, Action<Exception> failure)
{
Client.GetTokenAsync(userLogin =>
{
var url = Client.BuildAuthorizeUrl(userLogin);
if (success != null) success(url);
}, error =>
{
if (failure != null) failure(error);
});
}
public void Authenticated(Action success, Action<Exception> failure)
{
Client.GetAccessTokenAsync((accessToken) =>
{
UserToken = accessToken.Token;
UserSecret = accessToken.Secret;
if (success != null) success();
},
(error) =>
{
if (failure != null) failure(error);
});
}
private void ShowException(Exception ex)
{
string error = ex.ToString();
}
}
I am able to authorize my app but am unsure how to save the access token. I'm presuming in app.config file but not sure.
Any help would be appreciated!
This is more of a .net question rather than a DropNet specific thing.
Have a look at this answer https://stackoverflow.com/a/3032538/75946 I don't agree with using the registry but the other 2 are good.
You will just need to load it from where ever you store it when you start up the app and set it on your DropNetClient instance.
Related
So, I have a scenario where I have implemented my own JWT authentication scheme and is the default authentication and challenge scheme in my Startup.cs:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
//code ommitted for brevity
})
.AddCookie("cookie")
.AddOpenIdConnect("facbook", async options =>
{
options.ResponseType = "code";
options.SignInScheme = "cookie";
//code ommitted for brevity
});
As you can see above that I have added AddOpenIdConnect and AddCookie for my external authentication. Now my question is that if I have a Redirect ActionMethod like this, how can return the Challenge scheme to point to my external one (facebook):
[HttpGet]
public async Task<IActionResult> Redirect()
{
var result = await HttpContext.AuthenticateAsync();
if (result.Succeeded)
{
return RedirectToAction("Index");
}
return Challenge("facebook");
}
This would also mean that my AuthenticateAsync would not work in this case since the default authentication scheme is pointing to JWT.
How can I add a this to my Challenge request and AuthenticateAsync method?
Thanks
To return the login page for your custom Identity Provider, you need to call the SignInManager.ConfigureExternalAuthenticationProperties() method. This method lets you define the redirectUrl and provider (you called your provider "facebook").
I have written it like this:
[Controller]
[Route("web/v2/[controller]")]
public class AccountController : Controller
{
private IAccountService accountService;
public AccountController(IAccountService accountService)
{
this.accountService = accountService;
}
// GET: web/Account/connect/{provider}
[AllowAnonymous]
[HttpGet("connect/{medium}/{provider}", Name = "web-v2-account-external-connect-challenge")]
public async Task<ActionResult> ExternalLogin([FromRoute]string medium, [FromRoute]string provider)
{
//var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { medium, provider });
var redirectUrl = Url.RouteUrl("web-v2-account-external-connect-callback", new { medium, provider });
var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
// GET: web/Account/connect/{provider}/callback
[HttpGet("connect/{medium}/{provider}/callback", Name = "web-v2-account-external-connect-callback")]
public async Task<ActionResult> ExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
{
try
{
var login_result = await accountService.PerfromExternalLogin();
if (login_result.Status)
{
var model = new LoginResultVM
{
Status = true,
Medium = medium,
Platform = login_result.Platform
};
return View(model);
}
else
{
var model = new LoginResultVM
{
Status = false,
Medium = medium,
Platform = login_result.Platform,
Error = login_result.Error,
ErrorDescription = login_result.ErrorDescription
};
return View(model);
}
}
catch (OtherAccountException otherAccountEx)
{
var model = new LoginResultVM
{
Status = false,
Medium = medium,
Platform = provider,
Error = "Could not login",
ErrorDescription = otherAccountEx.Message
};
return View(model);
}
catch (Exception ex)
{
var model = new LoginResultVM
{
Status = false,
Medium = medium,
Platform = provider,
Error = "Could not login",
ErrorDescription = "There was an error with your social login"
};
return View(model);
}
}
}
While my AccountRepository looks like this:
internal interface IAccountRepository
{
...
}
internal class AccountRepository : IAccountRepository
{
private MintPlayerContext mintplayer_context;
private UserManager<Entities.User> user_manager;
private SignInManager<Entities.User> signin_manager;
private JwtIssuerOptions jwtIssuerOptions;
public AccountRepository(UserManager<Entities.User> user_manager, SignInManager<Entities.User> signin_manager, MintPlayerContext mintplayer_context, IOptions<JwtIssuerOptions> jwtIssuerOptions)
{
this.user_manager = user_manager;
this.signin_manager = signin_manager;
this.mintplayer_context = mintplayer_context;
this.jwtIssuerOptions = jwtIssuerOptions.Value;
}
public async Task<Tuple<User, string>> Register(User user, string password)
{
...
}
public async Task<LoginResult> LocalLogin(string email, string password, bool createCookie)
{
...
}
public async Task<IEnumerable<AuthenticationScheme>> GetExternalLoginProviders()
{
var providers = await signin_manager.GetExternalAuthenticationSchemesAsync();
return providers.ToList();
}
public Task<AuthenticationProperties> ConfigureExternalAuthenticationProperties(string provider, string redirectUrl)
{
var properties = signin_manager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Task.FromResult(properties);
}
public async Task<LoginResult> PerfromExternalLogin()
{
var info = await signin_manager.GetExternalLoginInfoAsync();
if (info == null)
throw new UnauthorizedAccessException();
var user = await user_manager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
if (user == null)
{
string username = info.Principal.FindFirstValue(ClaimTypes.Name);
string email = info.Principal.FindFirstValue(ClaimTypes.Email);
var new_user = new Entities.User
{
UserName = username,
Email = email,
PictureUrl = null
};
var id_result = await user_manager.CreateAsync(new_user);
if (id_result.Succeeded)
{
user = new_user;
}
else
{
// User creation failed, probably because the email address is already present in the database
if (id_result.Errors.Any(e => e.Code == "DuplicateEmail"))
{
var existing = await user_manager.FindByEmailAsync(email);
var existing_logins = await user_manager.GetLoginsAsync(existing);
if (existing_logins.Any())
{
throw new OtherAccountException(existing_logins);
}
else
{
throw new Exception("Could not create account from social profile");
}
}
else
{
throw new Exception("Could not create account from social profile");
}
}
await user_manager.AddLoginAsync(user, new UserLoginInfo(info.LoginProvider, info.ProviderKey, info.ProviderDisplayName));
}
await signin_manager.SignInAsync(user, true);
return new LoginResult
{
Status = true,
Platform = info.LoginProvider,
User = ToDto(user)
};
}
public async Task<IEnumerable<UserLoginInfo>> GetExternalLogins(ClaimsPrincipal userProperty)
{
...
}
public async Task AddExternalLogin(ClaimsPrincipal userProperty)
{
...
}
public async Task RemoveExternalLogin(ClaimsPrincipal userProperty, string provider)
{
...
}
public async Task<User> GetCurrentUser(ClaimsPrincipal userProperty)
{
var user = await user_manager.GetUserAsync(userProperty);
return ToDto(user);
}
public async Task Logout()
{
await signin_manager.SignOutAsync();
}
#region Helper methods
private string CreateToken(Entities.User user)
{
...
}
#endregion
#region Conversion methods
internal static Entities.User ToEntity(User user)
{
...
}
internal static User ToDto(Entities.User user)
{
...
}
#endregion
}
I tried to add the MaxLoginAttempts feature in my ServiceStack project. But it is still allowing login attempts after 5 unsuccessful login attempts. I'm not sure to know what is missing in my code.
AppHost.cs :
public override void Configure(Funq.Container container)
{
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
Routes
.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name*}");
var appSettings = new AppSettings();
//Enable a custom authentication
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomAuthProvider(appSettings),
})
{
MaxLoginAttempts = 5
});
}
CustomAuthProvider.cs
public CustomAuthProvider(AppSettings appSettings) : base(appSettings) {}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// authentication logic
if (userName.Equals("username") && password.Equals("password"))
{
return true;
}
else
{
return false;
}
}
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
//Saving the session!
return base.OnAuthenticated(authService, session, tokens, authInfo);
}
The MaxLoginAttempts gets validated when you validate the Username/Password against the AuthRepository:
public virtual bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var authRepo = GetUserAuthRepository(authService.Request);
using (authRepo as IDisposable)
{
var session = authService.GetSession();
if (authRepo.TryAuthenticate(userName, password, out var userAuth))
{
if (IsAccountLocked(authRepo, userAuth))
throw new AuthenticationException(ErrorMessages.UserAccountLocked.Localize(authService.Request));
session.PopulateSession(userAuth, authRepo);
return true;
}
return false;
}
}
Since you're overriding TryAuthenticate() and not using an Auth Repository you're going to have to validate it yourself where you'll need to maintain a InvalidLoginAttempts counter wherever you're persisting the User Info.
If it helps this is what all Auth Repositories use to validate invalid password attempts:
public static void RecordInvalidLoginAttempt(this IUserAuthRepository repo, IUserAuth userAuth)
{
var feature = HostContext.GetPlugin<AuthFeature>();
if (feature?.MaxLoginAttempts == null) return;
userAuth.InvalidLoginAttempts += 1;
userAuth.LastLoginAttempt = userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.InvalidLoginAttempts >= feature.MaxLoginAttempts.Value)
{
userAuth.LockedDate = userAuth.LastLoginAttempt;
}
repo.SaveUserAuth(userAuth);
}
Note: the recommend way to set Camel Case is to use:
SetConfig(new HostConfig { UseCamelCase = true });
For all other Serialization customization you should use JsConfig.Init(), e.g:
JsConfig.Init(new Config {
DateHandler = DateHandler.ISO8601,
AlwaysUseUtc = true,
TextCase = TextCase.CamelCase,
ExcludeDefaultValues = true,
});
I want to implement client certificate authentication in my xamarin app.
On top of that I am using a custom Certificate Authority (CA) and TLS 1.2.
Until now I managed to get it running using android, UWP and WPF. The only platform missing is ios.
Here is my NSUrlSessionDelegate:
public class SSLSessionDelegate : NSUrlSessionDelegate, INSUrlSessionDelegate
{
private NSUrlCredential Credential { get; set; }
private SecIdentity identity = null;
private X509Certificate2 ClientCertificate = null;
private readonly SecCertificate CACertificate = null;
public SSLSessionDelegate(byte[] caCert) : base()
{
if (caCert != null)
{
CACertificate = new SecCertificate(new X509Certificate2(caCert));
}
}
public void SetClientCertificate(byte[] pkcs12, char[] password)
{
if (pkcs12 != null)
{
ClientCertificate = new X509Certificate2(pkcs12, new string(password));
identity = SecIdentity.Import(ClientCertificate);
SecCertificate certificate = new SecCertificate(ClientCertificate);
SecCertificate[] certificates = { certificate };
Credential = NSUrlCredential.FromIdentityCertificatesPersistance(identity, certificates, NSUrlCredentialPersistence.ForSession);
}
else
{
ClientCertificate = null;
identity = null;
Credential = null;
}
}
public override void DidReceiveChallenge(NSUrlSession session, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
if (challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodClientCertificate)
{
NSUrlCredential c = Credential;
if (c != null)
{
completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.UseCredential, c);
return;
}
}
if (challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodServerTrust)
{
SecTrust secTrust = challenge.ProtectionSpace.ServerSecTrust;
secTrust.SetAnchorCertificates(new SecCertificate[] {
CACertificate
});
secTrust.SetAnchorCertificatesOnly(true);
}
completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, null);
}
}
This works if no client certificate is configured DidReceiveChallenge is called once with AuthenticationMethodServerTrust and the custom CA is accepted.
But as soon as a client certificate is configured DidReceiveChallenge gets called 4 times (twice for each AuthenticationMethod) and I am getting NSURLErrorDomain (-1200) error.
Anyone any idea what I am doing wrong?
Update
The SSLSessionDelegate is used like this:
public class HttpsServer : AbstractRemoteServer, IRemoteServer
{
private static readonly Logger LOG = LogManager.GetLogger();
private SSLSessionDelegate sSLSessionDelegate;
private NSUrlSession session;
private NSUrl baseAddress;
public HttpsServer()
{
sSLSessionDelegate = new SSLSessionDelegate(SSLSupport.GetTruststoreRaw());
NSUrlSessionConfiguration configuration = NSUrlSessionConfiguration.DefaultSessionConfiguration;
configuration.HttpShouldSetCookies = true;
configuration.TimeoutIntervalForRequest = 30;
configuration.TLSMinimumSupportedProtocol = SslProtocol.Tls_1_2;
configuration.TimeoutIntervalForResource = 30;
NSMutableDictionary requestHeaders;
if (configuration.HttpAdditionalHeaders != null)
{
requestHeaders = (NSMutableDictionary)configuration.HttpAdditionalHeaders.MutableCopy();
}
else
{
requestHeaders = new NSMutableDictionary();
}
AppendHeaders(requestHeaders, SSLSupport.GetDefaultHeaders());
configuration.HttpAdditionalHeaders = requestHeaders;
session = NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)sSLSessionDelegate, NSOperationQueue.MainQueue);
baseAddress = NSUrl.FromString(SSLSupport.GetBaseAddress());
}
public void SetClientCertificate(byte[] pkcs12, char[] password)
{
sSLSessionDelegate.SetClientCertificate(pkcs12, password);
}
public override async Task<string> GetString(string url, Dictionary<string, string> headers, CancellationToken cancellationToken)
{
NSData responseContent = await GetRaw(url, headers, cancellationToken);
return NSString.FromData(responseContent, NSStringEncoding.UTF8).ToString();
}
private async Task<NSData> GetRaw(string url, Dictionary<string, string> headers, CancellationToken cancellationToken)
{
NSMutableUrlRequest request = GetRequest(url);
request.HttpMethod = "GET";
request.Headers = AppendHeaders(request.Headers, headers);
Task<NSUrlSessionDataTaskRequest> taskRequest = session.CreateDataTaskAsync(request, out NSUrlSessionDataTask task);
cancellationToken.Register(() =>
{
if (task != null)
{
task.Cancel();
}
});
try
{
task.Resume();
NSUrlSessionDataTaskRequest taskResponse = await taskRequest;
if (taskResponse == null || taskResponse.Response == null)
{
throw new Exception(task.Error.Description);
}
else
{
NSHttpUrlResponse httpResponse = (NSHttpUrlResponse)taskResponse.Response;
if (httpResponse.StatusCode == 303)
{
if (!httpResponse.AllHeaderFields.TryGetValue(new NSString("Location"), out NSObject locationValue))
{
throw new Exception("redirect received without Location-header!");
}
return await GetRaw(locationValue.ToString(), headers, cancellationToken);
}
if (httpResponse.StatusCode != 200)
{
throw new Exception("unsupported statuscode: " + httpResponse.Description);
}
return taskResponse.Data;
}
}
catch (Exception ex)
{
throw new Exception("communication exception: " + ex.Message);
}
}
}
And here my Info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>XXXXXXXXXX</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Update 2
Neither I found the solution nor could anyone give me a hint, so I finally dropped client-certificates for now. I switched to OAuth2 for authorization and use my own certificate-authority (no self-signed certificate) for server -authentication which works well.
But still I am interested in this issue and glad for every idea in how to make it work.
I would suggest using ModernHttpClient. It supports ClientCertificates for Android and iOS.
It's opensource so you could always check out their github for reference if you want to finish your own implementation.
ModernHttpClient
I try understand how work with api (based on api.vk.com).
I have created ASP.NET MVC 4 empty project.
It part of Controller:
public ActionResult LoginVk()
{
vkProvider = new VKProvider();
vkProvider.Config = new VkAppConfig { AppKey = "5572789", AppSecret = "i2OpN7gj62ddwTqqRJrK" };
return Redirect(vkProvider.Authorize("http://localhost:56287/User/Access"));
}
public string Authorize(string redirectTo)
{
return string.Format(AuthorizeUri, Config.AppKey, redirectTo);
}
public ActionResult Access()
{
if (Request.Params.AllKeys.Contains("code"))
{
var code = Request.Params["code"];
if (ProcessVkCode(code))
{
return RedirectToAction("List");
}
}
return View("Error");
}
protected bool ProcessVkCode(string code)
{
if (vkProvider.GetAccessToken(code))
{
var jsonVkAccess = JsonConvert.SerializeObject(vkProvider.AccessToken);
var jObj = vkProvider.GetUserInfo();
var vkUser = new User
{
FirstName = jObj.ToString(),
LastName = jsonVkAccess.ToString()
};
repository.SaveUser(vkUser);
return true;
}
return false;
}
It's part of model VKProvider:
public static string AuthorizeUri =
"http://api.vkontakte.ru/oauth/authorize?client_id={0}&scope=photos,offline,wall,groups&redirect_uri={1}&response_type=code";
public static string GetTokenUri =
"https://api.vkontakte.ru/oauth/access_token?client_id={0}&client_secret={1}&code={2}";
public bool GetAccessToken(string Code)
{
try
{
string reqStr = string.Format(GetTokenUri, Config.AppKey, Config.AppSecret, Code);
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
WebClient webClient = new WebClient();
var response = webClient.DownloadString(reqStr);
AccessToken = JsonConvert.DeserializeObject<VkAccessToken>(response);
return true;
}
catch (WebException ex)
{
return false;
}
}
Application return error (401) Unauthorized when execute webClient.DownloadString(reqStr). What is wrong?
I have an existing, working ASP.NET MVC 4 web application. I have written my own RoleProvider and I am using the standard [Authorize] attribute. My controllers look like this:
[Authorize(Roles="ContactAdmins")] //System.Web.Mvc
public ActionResult Index()
I would like to add a WebAPI controller to my application, and take advantage of my existing plumbing
[Authorize(Roles="ContactAdmins")] //System.Web.Http
public IEnumerable<Contact> Get()
This works for Javascript ajax calls from within my site (since the browser user is already authenticated with a Forms auth cookie). My question is from a C# Console Application (or any other application that is not part of my web app) how can I authenticate to this API?
Lets assume that for the parts of my API which are public, I am using code very similar to what is found at this question Consuming WebApi in MVC3.
var url = "http://localhost:9000/api/contacts";
using (var client = new WebClient())
using (var reader = XmlReader.Create(client.OpenRead(url)))
{
var serializer = new XmlSerializer(typeof(Contact[]));
var contacts = (Contact[])serializer.Deserialize(reader);
// TODO: Do something with the contacts
}
What would I need to modify here? Or would I have to scrap this and use a completely different approach? I am not tied to using Forms for API Authentication of remote clients, but I would like to keep the current elegant approach for JavaScript clients that are part of the app (just request API since forms cookie is set).
You could combine the standard Forms Auth with a custom Basic Auth, based on the same primitives than Forms Auth. Note with Basic, HTTPS is strongly recommended (and in fact more and more Windows component do not support Basic+HTTP by default nowadays).
Here is a sample code for a Basic Authentication Module that reuses code from Forms Auth. It also comes with it's own configuration section (named 'basicAuth'). You want to make sure both auths (Forms and Basic) use the same cookie and parameters when configured together:
using System;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.Net;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
namespace MySecurity
{
public class BasicAuthenticationModule : IHttpModule
{
public event EventHandler<BasicAuthenticationEventArgs> Authenticate;
public void Dispose()
{
}
protected virtual string GetRealm(HttpContext context)
{
return BasicAuthenticationSection.Current.GetRealm(context);
}
public virtual void Init(HttpApplication context)
{
context.AuthenticateRequest += OnAuthenticateRequest;
context.EndRequest += OnEndRequest;
}
protected virtual bool FormsAuthenticate(HttpContext context, string login, string password, string realm)
{
// check ad-hoc forms credentials, as we can support it even if forms auth is not configured
FormsAuthenticationConfiguration c = ((AuthenticationSection)ConfigurationManager.GetSection("system.web/authentication")).Forms;
if ((c.Credentials == null) || (c.Credentials.Users == null))
return false;
foreach (FormsAuthenticationUser user in c.Credentials.Users)
{
if ((string.Compare(user.Name, login, true, CultureInfo.CurrentCulture) == 0) &&
(string.Compare(user.Password, password, true, CultureInfo.CurrentCulture) == 0))
return true;
}
return false;
}
protected virtual bool OnAuthenticate(HttpContext context, string login, string password, string realm)
{
EventHandler<BasicAuthenticationEventArgs> handler = Authenticate;
if (handler != null)
{
BasicAuthenticationEventArgs e = new BasicAuthenticationEventArgs(context, login, password, realm);
handler(this, e);
return !e.Cancel;
}
return FormsAuthenticate(context, login, password, realm);
}
protected virtual string[] GetUserRoles(HttpContext context, string login, string realm)
{
// TODO: overwrite if needed
return new string[0];
}
protected virtual IPrincipal GetUser(HttpContext context, FormsAuthenticationTicket ticket)
{
return new GenericPrincipal(new BasicAuthenticationIdentity(ticket), GetUserRoles(context, ticket.Name, GetRealm(context)));
}
protected virtual void OnAuthenticated(HttpContext context)
{
}
protected virtual void OnEndRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
if (application.Response.StatusCode != (int)HttpStatusCode.Unauthorized)
return;
string basic = "Basic Realm=\"" + GetRealm(application.Context) + "\"";
application.Response.AppendHeader("WWW-Authenticate", basic);
}
public static void SignOut()
{
if (HttpContext.Current == null)
return;
HttpContext.Current.Request.Cookies.Remove(BasicAuthenticationSection.Current.Name);
HttpContext.Current.Response.Cookies.Remove(BasicAuthenticationSection.Current.Name);
HttpCookie cookie = new HttpCookie(BasicAuthenticationSection.Current.Name);
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
public static bool IsAuthenticated(HttpContext context)
{
if ((context == null) || (context.User == null) || (context.User.Identity == null))
return false;
return context.User.Identity.IsAuthenticated;
}
protected virtual void OnAuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
if ((IsAuthenticated(application.Context)) && (!BasicAuthenticationSection.Current.ReAuthenticate))
return;
string encryptedTicket;
FormsAuthenticationTicket ticket;
HttpCookie cookie = application.Context.Request.Cookies[BasicAuthenticationSection.Current.Name];
if (cookie == null)
{
// no cookie, check auth header
string authHeader = application.Context.Request.Headers["Authorization"];
if ((string.IsNullOrEmpty(authHeader)) || (!authHeader.StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase)))
{
ResponseAccessDenied(application);
return;
}
string login;
string password;
string lp = authHeader.Substring(6).Trim();
if (string.IsNullOrEmpty(lp))
{
ResponseAccessDenied(application);
return;
}
lp = Encoding.Default.GetString(Convert.FromBase64String(lp));
if (string.IsNullOrEmpty(lp.Trim()))
{
ResponseAccessDenied(application);
return;
}
int pos = lp.IndexOf(':');
if (pos < 0)
{
login = lp;
password = string.Empty;
}
else
{
login = lp.Substring(0, pos).Trim();
password = lp.Substring(pos + 1).Trim();
}
if (!OnAuthenticate(application.Context, login, password, GetRealm(application.Context)))
{
ResponseAccessDenied(application);
return;
}
// send cookie back to client
ticket = new FormsAuthenticationTicket(login, false, (int)BasicAuthenticationSection.Current.Timeout.TotalMinutes);
encryptedTicket = FormsAuthentication.Encrypt(ticket);
cookie = new HttpCookie(BasicAuthenticationSection.Current.Name, encryptedTicket);
application.Context.Response.Cookies.Add(cookie);
// don't overwrite context user if it's been set
if ((!IsAuthenticated(application.Context)) || (BasicAuthenticationSection.Current.ReAuthenticate))
{
application.Context.User = GetUser(application.Context, ticket);
}
OnAuthenticated(application.Context);
application.Context.Response.StatusCode = (int)HttpStatusCode.OK;
return;
}
// there is a cookie, check it
encryptedTicket = cookie.Value;
if (string.IsNullOrEmpty(encryptedTicket))
{
ResponseAccessDenied(application);
return;
}
try
{
ticket = FormsAuthentication.Decrypt(encryptedTicket);
}
catch
{
ResponseAccessDenied(application);
return;
}
if (ticket.Expired)
{
ResponseAccessDenied(application);
return;
}
// set context user
// don't overwrite context user if it's been set
if ((!IsAuthenticated(application.Context) || (BasicAuthenticationSection.Current.ReAuthenticate)))
{
application.Context.User = GetUser(application.Context, ticket);
}
OnAuthenticated(application.Context);
}
protected virtual void WriteAccessDenied(HttpApplication application)
{
if (application == null)
throw new ArgumentNullException("application");
application.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
application.Context.Response.StatusDescription = "Unauthorized";
application.Context.Response.Write(application.Context.Response.StatusCode + " " + application.Context.Response.StatusDescription);
}
protected virtual void ResponseAccessDenied(HttpApplication application)
{
// if there is a bad cookie, kill it
application.Context.Request.Cookies.Remove(BasicAuthenticationSection.Current.Name);
application.Context.Response.Cookies.Remove(BasicAuthenticationSection.Current.Name);
HttpCookie cookie = new HttpCookie(BasicAuthenticationSection.Current.Name);
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
WriteAccessDenied(application);
application.CompleteRequest();
}
}
public class BasicAuthenticationSection : ConfigurationSection
{
public const string SectionName = "basicAuth";
private const string DefaultCookieName = "." + SectionName;
private static BasicAuthenticationSection _current;
public static BasicAuthenticationSection Current
{
get
{
return _current ?? (_current = ConfigurationManager.GetSection(SectionName) as BasicAuthenticationSection ?? new BasicAuthenticationSection());
}
}
[StringValidator(MinLength = 1), ConfigurationProperty("name", DefaultValue = DefaultCookieName)]
public string Name
{
get
{
return (string)base["name"];
}
}
internal string GetRealm(HttpContext context)
{
if (!string.IsNullOrEmpty(Realm))
return Realm;
return context.Request.Url.Host;
}
[ConfigurationProperty("realm", DefaultValue = "")]
public string Realm
{
get
{
return (string)base["realm"];
}
}
[ConfigurationProperty("domain", DefaultValue = "")]
public string Domain
{
get
{
return (string)base["domain"];
}
}
[ConfigurationProperty("reAuthenticate", DefaultValue = false)]
public bool ReAuthenticate
{
get
{
return (bool)base["reAuthenticate"];
}
}
[TypeConverter(typeof(TimeSpanMinutesConverter)), ConfigurationProperty("timeout", DefaultValue = "30"), PositiveTimeSpanValidator]
public TimeSpan Timeout
{
get
{
return (TimeSpan)base["timeout"];
}
}
}
public class BasicAuthenticationIdentity : IIdentity
{
public BasicAuthenticationIdentity(FormsAuthenticationTicket ticket)
{
if (ticket == null)
throw new ArgumentNullException("ticket");
Ticket = ticket;
}
public FormsAuthenticationTicket Ticket;
public string AuthenticationType
{
get
{
return BasicAuthenticationSection.SectionName;
}
}
public bool IsAuthenticated
{
get
{
return true;
}
}
public string Name
{
get
{
return Ticket.Name;
}
}
}
public class BasicAuthenticationEventArgs : CancelEventArgs
{
public BasicAuthenticationEventArgs(HttpContext context, string login, string password, string realm)
{
if (context == null)
throw new ArgumentNullException("context");
Context = context;
Login = login;
Password = password;
Realm = realm;
}
public HttpContext Context { get; private set; }
public string Realm { get; private set; }
public string Login { get; private set; }
public string Password { get; private set; }
public IPrincipal User { get; set; }
}
}
Once this is installed server side, you can configure the WebClient to use Basic auth:
WebClient client = new WebClient();
client.Credentials = new NetworkCredential("username", "password");
There are numerous ways to share the cookie with the console application. Take a look at some ideas here:
http://netpl.blogspot.com/2008/02/clickonce-webservice-and-shared-forms.html
Another simple option would be to expose a web method which doesn't require any authentication, gets the username and password and returns the cookie to the client.
No matter what approach you take, your goal is to somehow get the forms cookie at the console application side. From there you are easily done as all you do is you attach the cookie to your requests. The web api will accept the cookie happily.