Fighting the authentication method

I want, without using WCF / C # built-in components for it,

  • RESTful Client Authentication
  • Handling authentication errors when calling an API in the client

This is a pedagogical exercise: I understand that there are built-in authentication methods, I want to do this from scratch in order to understand how it all works.

I have hash and password validation logic and an open REST call that validates the password, but I'm not sure how it gets from here.

Background

I am trying to create an authentication method for my holiday service.

So far, I have managed to create a password hash, salt and save the salt, and I have been able to authenticate the user. However, I'm not sure how you should encapsulate all my REST request requests on wcf so that if they were requested (GET, POST), it asks you to log in and if your logon is missing.

Because I have included my own authentication method, and I'm new to web services, and C # I really don't know where to start?

So, I am going to offer 300 representatives of all who could offer an approach to this.

The code

This is my holiday service:

[ServiceContract(Namespace = "http://tempuri.org")] [XmlSerializerFormat] public interface IService { .... all of my GET, POST, PUT and DELETE requests { [DataContract(Name="Student")] [Serializable] public class Student { [DataMember(Name = "StudentID")] public string StudentID { get; set; } [DataMember(Name = "FirstName")] public string FirstName { get; set; } [DataMember(Name = "LastName")] public string LastName { get; set; } [DataMember(Name = "Password")] public string Password; [DataMember(Name = "Salt")] public byte[] Salt; //note the use of public datamembers for password and salt, not sure how to implement private for this. } [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] [Serializable] public class Service: IService { #region Authentication, hash and salt protected RNGCryptoServiceProvider random = new RNGCryptoServiceProvider(); public byte[] GenerateSalt() //Generate random salt for each password { byte[] salt = new byte[10000]; random.GetNonZeroBytes(salt); return salt; } public static byte[] Hash(string value, byte[] salt) //hash and salt the password { return Hash(Encoding.UTF8.GetBytes(value), salt); } public static byte[] Hash(byte[] value, byte[] salt) // create hash of password { byte[] saltedValue = value.Concat(salt).ToArray(); return new SHA256Managed().ComputeHash(saltedValue); //initialise new isntance of the crypto class using SHA-256/32-byte (256 bits) words } public string AuthenticateUser(string studentID, string password) //Authentication should always be done server side { var result = students.FirstOrDefault(n => n.StudentID == studentID); //find the StudentID that matches the string studentID if (result != null) //if result matches then do this { byte[] passwordHash = Hash(password, result.Salt); string HashedPassword = Convert.ToBase64String(passwordHash); //hash salt the string password if (HashedPassword == result.Password) //check if the HashedPassword (string password) matches the stored student.Password { return result.StudentID; // if it does return the Students ID } } return "Login Failed"; //if it doesnt return login failed } #endregion 

I am also hosting from a console application and I do not have web.config files or app.config files. And since I made my own authentication method, I'm not sure if basic authentication will work.

I also do not want to have a session to support SOA services and Stateless.

Console Application:

 namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); WebHttpBinding binding = new WebHttpBinding(); binding.Security.Mode = WebHttpSecurityMode.Transport; host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior()); host.Open(); Console.WriteLine("Host opened"); Console.ReadLine(); } } } 

Please note that on my client side I am doing something very simple for authentication:

  private void Login_Click(object sender, RoutedEventArgs e) { //Authenticate user (GET Request) string uri = string.Format("http://localhost:8000/Service/AuthenticateUser/{0}/{1}", textBox1.Text, passwordBox1.Password); XDocument xDoc = XDocument.Load(uri); string UserAuthenticationID = xDoc.Element("string").Value; Int32 value; if (Int32.TryParse(UserAuthenticationID, out value)) { MainWindow authenticatedidentification = new MainWindow(); authenticatedidentification.SetLabel(UserAuthenticationID); authenticatedidentification.Show(); this.Close(); } else { label1.Content = UserAuthenticationID; } } 

Therefore, I am not sure what else would need to be placed in the main application, if there is anything for the above, so that the main application gets access to these requests for rest.

+6
source share
4 answers

So, as is usually done,

  • the client provides some credentials through an authentication service call
  • the service verifies these credentials and returns some authentication information.
  • Subsequent calls use this token for authentication.

    This is done by sending a token (for example, email authentication ) or more reliably, a token is the key that is used to calculate the message authentication code on the parameters. This does not allow anyone to manipulate requests.

There is a decent, albeit long, discussion of how to do this in WCF here . See the Security Issues section and the Authentication and Authorization Implementation section.

So, let's say you did this (or sending username and password with each request is a bad idea, but hey, this is for educational purposes only) and you have an AuthenticateUser method that returns false if users are not authenticated. Now in every open REST method you add this call (with parameters that are username and passwords or authentication token)

 if (!AuthenticateUser(/* auth params here */)) { WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized; return; } 

This will cause the request to fail, and the client will receive a 403 Forbiden HTTP request.

I assume that you are using HttpWebRequest to make REST API calls.

So, in your client program, after you have prepared the request, added all the necessary parameters, do it

 try { var wResp = (HttpWebResponse)wReq.GetResponse(); var wRespStatusCode = wResp.StatusCode; } catch (WebException we) { var wRespStatusCode = ((HttpWebResponse)we.Response).StatusCode; if( wRespStatusCode == HttpStatusCode. Unauthorized) { // call to your sign in / login logic here } else{ throw we; } } 

You need to include the authentication token somehow in the request, either as get or post paramater, or in the header. Post or Get is just a matter of adding paramater to the request data. The title is a little more complicated, I believe that it is set out in the MSDN link that I mentioned above.

0
source

Why not use OAuth or OpenID for your REST service ?! There is OAuth 2.0 or earlier. There are also implementations for the client and server. OAuth protocol is suitable for REST services

You do not need to create your own mechanism.

The main site for OAuth is http://oauth.net/code/ There you can find a description of OAuth, threads, etc. There are also links to implementations, for example. Dotnetpenpen

The latest specification is http://tools.ietf.org/html/draft-ietf-oauth-v2.

You can find many samples for implementing DotNetOAuth OAuth on their Github repository https://github.com/AArnott/dotnetopenid/tree/master/samples

0
source

@jbtule and @Damien_The_Unbeliever make great salt storage recommendations with a hashed password.

As for your question on how to implement it, I would not do it as a separate service method, but instead do part of the authentication of the method itself. The client then needs to transfer the credentials using a service call.

This link describes in detail how to do this, how it looks from the server and client, etc.

Edit: instead of passing the username and password in the message credentials, as in the link above, you can pass the login token and just check that it is valid on the web service before executing the request.

-1
source

As I recently (the last couple of weeks) did this through IDispatchMessageInspector. In the message inspector class, I used securityContext.AuthorizationContext.ClaimSets to verify the client (caller) certificate, but you can use the custom header (User, Password) and look at OperationContext.Current.IncomingMessageHeaders. And in AfterReceiveRequest (), I would either throw an error if the user was not a valid user, or simply return null to indicate success.

Then I created an attribute that will add my inspector (MessageInspector) to the service class:

 [AttributeUsage(AttributeTargets.Class)] public class AuthorizeAttribute : Attribute, IServiceBehavior { public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcherBase dispatcher in serviceHostBase.ChannelDispatchers) { var channelDispatcher = dispatcher as ChannelDispatcher; if (channelDispatcher != null) { foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) { var inspector = new MessageInspector(); endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector); } } } //var config = new ServiceLayerConfiguration(); //config.RequestProcessorImplementation = typeof(PassThruRequestProcessor); //config.Initialize(); } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } } 

And finally, in the service class, I just add an attribute.

 [AuthorizeAttribute] public class OperaService : IMyService 

If necessary, I can provide more detailed information. I still have the client / service application on my inbox. :)

-2
source

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


All Articles