Basic Authentication Middleware with OWIN and ASP.NET WEB API -
i created asp.net web api 2.2 project. used windows identity foundation based template individual accounts available in visual studio see here.
the web client (written in angularjs) uses oauth implementation web browser cookies store token , refresh token. benefit helpful usermanager , rolemanager classes managing users , roles. works fine oauth , web browser client.
however, retro-compatibility concerns desktop based clients need support basic authentication. ideally, [authorize], [authorize(role = "administrators")] etc. attributes work both oauth , basic authentication scheme.
thus, following code leastprivilege created owin basicauthenticationmiddleware inherits authenticationmiddleware. came following implementation. basicauthenticationmiddleware handler has changed compared leastprivilege's code. use claimsidentity rather series of claim.
class basicauthenticationhandler: authenticationhandler<basicauthenticationoptions> { private readonly string _challenge; public basicauthenticationhandler(basicauthenticationoptions options) { _challenge = "basic realm=" + options.realm; } protected override async task<authenticationticket> authenticatecoreasync() { var authzvalue = request.headers.get("authorization"); if (string.isnullorempty(authzvalue) || !authzvalue.startswith("basic ", stringcomparison.ordinalignorecase)) { return null; } var token = authzvalue.substring("basic ".length).trim(); var claimsidentity = await trygetprincipalfrombasiccredentials(token, options.credentialvalidationfunction); if (claimsidentity == null) { return null; } else { request.user = new claimsprincipal(claimsidentity); return new authenticationticket(claimsidentity, new authenticationproperties()); } } protected override task applyresponsechallengeasync() { if (response.statuscode == 401) { var challenge = helper.lookupchallenge(options.authenticationtype, options.authenticationmode); if (challenge != null) { response.headers.appendvalues("www-authenticate", _challenge); } } return task.fromresult<object>(null); } async task<claimsidentity> trygetprincipalfrombasiccredentials(string credentials, basicauthenticationmiddleware.credentialvalidationfunction validate) { string pair; try { pair = encoding.utf8.getstring( convert.frombase64string(credentials)); } catch (formatexception) { return null; } catch (argumentexception) { return null; } var ix = pair.indexof(':'); if (ix == -1) { return null; } var username = pair.substring(0, ix); var pw = pair.substring(ix + 1); return await validate(username, pw); }
then in startup.auth declare following delegate validating authentication (simply checks if user exists , if password right , generates associated claimsidentity)
public void configureauth(iappbuilder app) { app.createperowincontext(dbcontext.create); app.createperowincontext<applicationusermanager>(applicationusermanager.create); func<string, string, task<claimsidentity>> validationcallback = (string username, string password) => { using (dbcontext dbcontext = new dbcontext()) using(userstore<applicationuser> userstore = new userstore<applicationuser>(dbcontext)) using(applicationusermanager usermanager = new applicationusermanager(userstore)) { var user = usermanager.findbyname(username); if (user == null) { return null; } bool ok = usermanager.checkpassword(user, password); if (!ok) { return null; } claimsidentity claimsidentity = usermanager.createidentity(user, defaultauthenticationtypes.applicationcookie); return task.fromresult(claimsidentity); } }; var basicauthoptions = new basicauthenticationoptions("kmailwebmanager", new basicauthenticationmiddleware.credentialvalidationfunction(validationcallback)); app.usebasicauthentication(basicauthoptions); // configure application oauth based flow publicclientid = "self"; oauthoptions = new oauthauthorizationserveroptions { tokenendpointpath = new pathstring("/token"), provider = new applicationoauthprovider(publicclientid), //if accesstokenexpiretimespan changed, change expiresutc in refreshtokenprovider.cs. accesstokenexpiretimespan = timespan.fromhours(2), allowinsecurehttp = true, refreshtokenprovider = new refreshtokenprovider() }; // enable application use bearer tokens authenticate users app.useoauthbearertokens(oauthoptions); }
however, settings request.user in handler's authenticationasynccore method [authorize] attribute not work expected: responding error 401 unauthorized every time try use basic authentication scheme. idea on going wrong?
i found out culprit, in webapiconfig.cs file 'individual user' template inserted following lines.
//// web api configuration , services //// configure web api use bearer token authentication. config.suppressdefaulthostauthentication(); config.filters.add(new hostauthenticationfilter(oauthdefaults.authenticationtype));
thus have register our basicauthenticationmiddleware
config.suppressdefaulthostauthentication(); config.filters.add(new hostauthenticationfilter(oauthdefaults.authenticationtype)); config.filters.add(new hostauthenticationfilter(basicauthenticationoptions.basicauthenticationtype));
where basicauthenticationtype constant string "basic" passed base constructor of basicauthenticationoptions
public class basicauthenticationoptions : authenticationoptions { public const string basicauthenticationtype = "basic"; public basicauthenticationmiddleware.credentialvalidationfunction credentialvalidationfunction { get; private set; } public basicauthenticationoptions( basicauthenticationmiddleware.credentialvalidationfunction validationfunction) : base(basicauthenticationtype) { credentialvalidationfunction = validationfunction; } }
Comments
Post a Comment