AngularJs ASP.NET WebApi Authentication with Thinktecture

I am trying to create an AngularJs website that sends login and password to the ASP.NET WebApi backend and logs this user in using Thinktecture.

I have a Thinktecture working perfectly with another project, ASP.NET MVC, using WS-Federation. Now I am trying to do something similar, but I am changing some components, and I can not get it to work.

How can I send a Thinktecture username and password from ASP.NET WebApi and verify it?

using System.Collections.Generic; using System.IdentityModel.Services; using System.Web.Http; using WebApi_AngularJs.Model; namespace WebApi_AngularJs.Controllers { public class AuthorizationController : ApiController { // POST: api/Authorization public LoginResponse Post([FromBody]Login data) { //HOW TO SEND data.user and data.password to ThinkTecture and get //response if user valid or not?? var response = new LoginResponse { access_token = "token", data = "data"}; return response; } } } 

Thanks!

+6
source share
2 answers

Finally, after a long read, I have the following:

In AngularJS:

 'use strict'; app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) { var serviceBase = 'http://localhost:64346/'; var authServiceFactory = {}; var _authData = localStorageService.get('authorizationData'); var _authentication = { isAuth: _authData != null? true : false, userName: _authData != null ? _authData.userName : "" }; var _saveRegistration = function (registration) { _logOut(); return $http.post(serviceBase + 'api/account/register', registration).then(function (response) { return response; }); }; var _login = function (loginData) { var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password; var deferred = $q.defer(); $http.post(serviceBase + 'api/authorization', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) { localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName }); _authentication.isAuth = true; _authentication.userName = loginData.userName; deferred.resolve(response); }).error(function (err, status) { _logOut(); deferred.reject(err); }); return deferred.promise; }; var _logOut = function () { $http.delete(serviceBase + 'api/authorization').success(function() { localStorageService.remove('authorizationData'); _authentication.isAuth = false; _authentication.userName = ""; }); }; var _fillAuthData = function () { var authData = localStorageService.get('authorizationData'); if (authData) { _authentication.isAuth = true; _authentication.userName = authData.userName; } } authServiceFactory.saveRegistration = _saveRegistration; authServiceFactory.login = _login; authServiceFactory.logOut = _logOut; authServiceFactory.fillAuthData = _fillAuthData; authServiceFactory.authentication = _authentication; return authServiceFactory; }]); 

In WebApi:

 using System.Collections.Generic; using System.Configuration; using System.IdentityModel.Protocols.WSTrust; using System.IdentityModel.Services; using System.IdentityModel.Tokens; using System.IO; using System.Linq; using System.Net; using System.Security.Claims; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Security; using System.Web.Http; using System.Xml; using Thinktecture.IdentityModel.Constants; using Thinktecture.IdentityModel.WSTrust; using WebApi_AngularJs.Model; namespace WebApi_AngularJs.Controllers { public class AuthorizationController : ApiController { // GET: api/Authorization public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET: api/Authorization/5 [Authorize] public string Get(int id) { return "value"; } // POST: api/Authorization public LoginResponse Post([FromBody]Login data) { var credentials = new ClientCredentials(); credentials.UserName.UserName = data.UserName; credentials.UserName.Password = data.Password; ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true; var claims = GetClaimsFromIdentityServer(data.UserName, data.Password); var response = new LoginResponse(); if (claims != null) { //All set so now create a SessionSecurityToken var token = new SessionSecurityToken(claims) { IsReferenceMode = true //this is //important.this is how you say create //the token in reference mode meaning //your session cookie will contain only a //referenceid(which is very small) and //all claims will be stored on the server }; FederatedAuthentication.WSFederationAuthenticationModule. SetPrincipalAndWriteSessionToken(token, true); response = new LoginResponse { access_token = token.Id , data = "data"}; } return response; } // PUT: api/Authorization/5 public void Put(int id, [FromBody]string value) { } // DELETE: api/Authorization/ public void Delete() { //clear local cookie FederatedAuthentication.SessionAuthenticationModule.SignOut(); FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie(); FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false); } private ClaimsPrincipal GetClaimsFromIdentityServer(string username, string password) { const string WS_TRUST_END_POINT = "https://srv:4443/issue/wstrust/mixed/username"; var factory = new System.ServiceModel.Security.WSTrustChannelFactory (new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), string.Format(WS_TRUST_END_POINT)); factory.TrustVersion = TrustVersion.WSTrust13; factory.Credentials.UserName.UserName = username; factory.Credentials.UserName.Password = password; var rst = new System.IdentityModel.Protocols.WSTrust.RequestSecurityToken { RequestType = RequestTypes.Issue, KeyType = KeyTypes.Bearer, TokenType = TokenTypes.Saml2TokenProfile11, AppliesTo = new EndpointReference ("urn:webapisecurity") }; var st = factory.CreateChannel().Issue(rst); var token = st as GenericXmlSecurityToken; var handlers = FederatedAuthentication.FederationConfiguration. IdentityConfiguration.SecurityTokenHandlers; var token = handlers.ReadToken(new XmlTextReader (new StringReader(token.TokenXml.OuterXml))) as Saml2SecurityToken; var identity = handlers.ValidateToken(token).First(); var principal = new ClaimsPrincipal(identity); return principal; } } } 

In Web.Config:

 <?xml version="1.0" encoding="utf-8"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=301879 --> <configuration> <configSections> <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <appSettings> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="ida:FederationMetadataLocation" value="https://srv:4443/FederationMetadata/2007-06/FederationMetadata.xml" /> <add key="ida:Realm" value="urn:webapisecurity" /> <add key="ida:AudienceUri" value="urn:webapisecurity" /> <add key="AppName" value="Web API Security Sample" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> </system.web> <system.webServer> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> <modules> <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> <system.identityModel> <identityConfiguration> <audienceUris> <add value="urn:webapisecurity" /> </audienceUris> <claimsAuthorizationManager type="Thinktecture.IdentityServer.Ofi.AuthorizationManager, Thinktecture.IdentityServer.Ofi, Version=1.0.0.0, Culture=neutral" /> <claimsAuthenticationManager type="Thinktecture.IdentityServer.Ofi.AuthenticationManager, Thinktecture.IdentityServer.Ofi, Version=1.0.0.0, Culture=neutral" /> <certificateValidation certificateValidationMode="None" /> <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <trustedIssuers> <add thumbprint="489116B0FCF14DF66D47AE272C3B9FD867D0E050" /> </trustedIssuers> </issuerNameRegistry> </identityConfiguration> </system.identityModel> <system.identityModel.services> <federationConfiguration> <cookieHandler requireSsl="false" /> <wsFederation passiveRedirectEnabled="true" issuer="https://srv:4443/issue/wsfed" realm="urn:webapisecurity" reply="http://localhost:64346/" requireHttps="false" /> </federationConfiguration> </system.identityModel.services> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" /> </dependentAssembly> </assemblyBinding> </runtime> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration> 

With this, I can see that the FedAuth cookie is set in the browser and it also does validation in WebApi.

+5
source

There are a few things you need to do. Create an OAuth client that will make token requests, and use this to get access tokens from the authentication server, which will allow you to access the web api endpoints. To do this, your OAuth client must have an implicit stream. You then make a login request to the Identity server, usually through a popup, so that your OAuth client can log in. You need to transfer your OAuth client data in the request line for the Idsrv login request, the OAuth client configuration is what you defined in your Idsrv admin panel for the OAuth client, you should parameterize this and add it to the oauth2 / authorzie url command:

 getIdpOauthEndpointUrl: function () { return "https://192.168.1.9/issue/oauth2/authorize"; }, getOAuthConfig: function () { return { client_id: "Your Oauth CLient ID that you specifie din Identity Server", scope: "The scope of your RP", response_type: "token", redirect_uri: "https://..YourAngularAppUrl/AuthCallback.html" }; } 

Then you open the login window:

 var url = authService.getIdpOauthEndpointUrl() + "?" + $.param(authService.getOAuthConfig()); window.open(url, "Login", "height=500,width=350"); 

In your OAuth inIdsrv client, you need to specify the redirect URL, in our case:

 https://YourAngularAppUrl/AuthCallback.html 

this is what you pass in the login request to the authentication server along with the OAuth client data. The AuthCallback.html page AuthCallback.html nothing but extract the access token returned by idsrv to this page in the query string and passes it to your angular application, as you do, but this token should be placed in your $http headers.

The access token can be retrieved on the AuthCallback.html page as follows:

 <script src="/Scripts/jquery-2.0.3.js"></script> <script src="/Scripts/jquery.ba-bbq.min.js"></script> <script type="text/javascript"> var params = $.deparam.fragment(location.hash.substring(1)); window.opener.oAuthCallback(params); window.close(); </script> 

The OAuthCallback function OAuthCallback defined on my wrapper page my index.html and is responsible for passing the token that it passed to my angular application and putting it in the $http headers.

 function oAuthCallback(OAUTHTOKEN) { angular.element(window.document).scope().setHttpAuthHeaderAndAuthenticate(OAUTHTOKEN); } 

The setHttpAuthHeaderAndAuthenticate() function is defined on my $rootScope , and it places the token in the $http authorizaiton headers:

$http.defaults.headers.common.Authorization = 'Bearer ' + OAUTHTOKEN["access_token"];

Check out this post by Christian Weyer. It does exactly what we do, but this knockout / web api app is another concept.

The next step is to tell the web api to accept the access token from idsrv, it's simple:

 public static void Configure(HttpConfiguration config) { var authConfig = new AuthenticationConfiguration(); authConfig.AddJsonWebToken( YourIdsrvSiteId, YourRpsScope/Relam,YourRpsSymmetricSigningKey ); config.MessageHandlers.Add(new AuthenticationHandler(authNConfig)); } 

Here you can also define ClaimsAuthenticationManager and ClaimsAuthorizationManager so that you can convert applications and provide grants / deny access to web api resources. Again, all this is covered by the post of Christian Weyer. Hope this helps.

+6
source

Source: https://habr.com/ru/post/970437/


All Articles