I have some methods in my UserService.
class UserService {
Token generateTokenForLogin(String phone);
User login(String phone, String token);
Token generateTokenForRegistration(String phone);
User register(String phone, String token);
Token generateTokenForForgotPassword(String phone);
User forgotPassword(String phone, String token, String newPassword);
User updateUser(UpdateUserRequest request);
}
Should I separate these methods to
class LoginService {
Token generateTokenForLogin(String phone);
User login(String phone, String token);
}
class RegistrationService {
Token generateTokenForRegistration(String phone);
User register(String phone, String token);
}
class UserPasswordService {
Token generateTokenForForgotPassword(String phone);
User forgotPassword(String phone, String token, String newPassword);
}
class UserService {
User updateUser(UpdateUserRequest request);
}
Each method have 10+ LOC, I want to know which is a better way, thanks.
In OOP there are the SOLID principles. Where the S stands for Single Responsibility, where each function/class should have a clearly defined purpose. From the looks of it, i'd say your UserService Class does not follow this principle. I would indeed separate out the functionality, but I would have a different approach. Separate it into 2 classes, User Service and Token Generator like....
class UserService {
User login(String phone, String token);
User register(String phone, String token);
User forgotPassword(String phone, String token, String newPassword);
User updateUser(UpdateUserRequest request);
}
class TokenGenerator {
String newLoginToken(String phone);
String newRegistrationToken(String phone);
String newForgotPasswordToken(String phone);
}
Now your classes are seperated appropriately. TokenGenerator is dedicated to generating tokens, and UserService is dedicated to user services.
Seperate note, I like your self-documenting function names, however it seems a little verbose. I changed the function names to be more concise. Lastly, any reason why your "Generate Token" functions return type Token, but the login, register and forgot password token takes type String for token? Use built in variable types to avoid unneeded complexity if you can.
For maximum flexibility and robustness I would suggest creating an interface iUserService. Check the GoogleAPI UserService Interface for an example
Lastly, i thinkIt's great that you are worried about the quality of your code. In the future, I would check out the Code Review Stack Exchange. This exchange is dedicated to these types of questions.
Related
ASP.NET Core Identity offers built-in 2fa providers such as SMS, phone and Authenticator app. They all trigger a 2fa flow in the backend (send an SMS, start a phone call, or just ask the user for a code from their auth app). However, we're required to use an external 2fa provider whose process involves redirecting the user to a page on their URL, which will then redirect back to a page on our end which verifies the 2fa token. This is not an OAuth flow.
IUserTwoFactorTokenProvider only offers a way to return a string token. I could abuse this by returning a URL and then redirecting in the UI but that seems a misuse of the interface. Still, I'd like to have ASP.NET Identity in charge of deciding which users require 2fa, but that means it also wants to do the 2fa itself.
public class ExternalTokenProvider : IUserTwoFactorTokenProvider<ApplicationUser>
{
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<ApplicationUser> manager, ApplicationUser user)
{
return true; //all users require 2fa
}
public async Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser> manager, ApplicationUser user)
{
var challenge = GenerateChallengeToken();
return $"https://2fa.example.com?user={user.Id}&token={challenge}";
}
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<ApplicationUser> manager, ApplicationUser user)
{
//validate the response token;
}
}
Looking at the implementation of AuthenticatorTokenProvider I see it simply returns string.Empty in GenerateAsync(), and then in Microsoft.AspNetCore.Identity.UI the LoginWith2fa.cshtml.cs page is called. I could do something similar but use the URL retrieved from GenerateAsync() to redirect the user.
I've also read How to register a Two-factor authentication provider which still just uses the basic functionality of sending a code via phone/e-mail. It seems the only choice you really have here is to choose the communication gateway.
Is this a proper way to do it within ASP.NET Identity? What's a better way to do this?
My implementation ended up not generating the redirect URL in the TokenProvider (because it doesn't seem to be possible to retrieve the generated token/URL from the frontend) but rather copying the implementation of Microsoft.AspNetCore.Identity.UI and just doing it in the frontend:
MfaTokenProvider.cs
public async Task<bool> CanGenerateTwoFactorTokenAsync(...)
{
return true;
}
public async Task<string> GenerateAsync(...)
{
return Task.FromResult(string.Empty);
}
public async Task<bool> ValidateAsync(...)
{
return _mfaProvider.ValidateToken(token, user.Email);
}
AccountController.cs
[HttpPost]
public async Task<IActionResult> Login(LoginForm form)
{
var result = await _signInManager.PasswordSignInAsync(form.Username, form.Password, form.RememberMe, false);
if (result.RequiresTwoFactor)
{
var authenticationUrl = _mfaProvider.GetUrl(form.Username, form.ReturnUrl);
return RedirectResult(authenticationUrl);
}
return RedirectResult(form.ReturnUrl);
}
This has the advantage of leaving RequiresTwoFactor managed by Identity. The downside is the implementation of the 2FA is split over 2 classes and can cause issues when needing to offer multiple 2FA options. This is not currently a requirement of my app, however.
I am using Spring Boot 1.3.3.RELEASE and Spring Data Rest for a project in which I want a WRITE_ONLY password field. That is...you can perform a POST with this field set, and it will be deserialized and usable within an event handler annotated with #HandleBeforeCreate. However, this field will only ever be used on creation, and after that it should never show up anywhere, hence WRITE_ONLY. I have tried solutions like this...
public class Person {
#JsonIgnore
String password;
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty("password")
public void setPassword(String password) {
this.password = password;
}
}
and this...
public class Person {
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
but it seems like as soon as #JsonIgnore or #JsonProperty make an appearance in any form or combination, Spring Data Rest stops deserializing the password field and I can no longer access it in my #HandleBeforeCreate annotated method.
This seems to contradict similar advice given here:
Set disallowed fields in Spring Data Rest
Ignoring property when deserializing
It also occurred to me that a projection could easily solve this problem, but it is my understanding that there is no way to provide a default projection for repositories, so the password field would show up unless a user included ?projection=noPassword in their request, which is not what I want.
I just want to use the password field during creation via POST, and have it be utterly forgotten about afterwards, never to be rendered again.
Is a bug filing in order, or have I overlooked something?
I am working on an MVC site that has some pages that need authentication and others that don't. This is determined using the Authorize and AllowAnonymous attributes in a pretty standard way. If they try to access something restricted they get redirected to the login page.
I'm now wanting to add the functionality to automatically log them in using an encrypted token passed in the querystring (the link will be in emails sent out). So the workflow I want now is that if a request goes to a page that is restricted and there is a login token in the querystring I want it to use that token to log in. If it logs in successfully then I want it to run the original page requested with the new logged in context. If it fails to log in then it will redirect to a custom error page.
My question is where would I need to insert this logic into the site?
I have seen some suggestions on subclassing the Authorize attribute and overriding some of the methods but I'm not 100% sure how to go about this (eg what I would override and what I'd do in those overridden methods.
I've also had a look at putting the logic at a controller level but I am led to understand that the authorize attribute would redirect it away from the controller before any code in the controller itself was run.
It would be better to write a custom authorization attribute that will entirely replace the default functionality and check for the query string parameter and if present, decrypt it and authenticate the user. If you are using FormsAuthentication that would be to call the FormsAuthentication.SetAuthCookie method. Something along the lines of:
public class TokenAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string token = filterContext.HttpContext.Request["token"];
IPrincipal user = this.GetUserFromToken(token);
if (user == null)
{
this.HandleUnAuthorizedRequest(filterContext);
}
else
{
FormsAuthentication.SetAuthCookie(user.Identity.Name, false);
filterContext.HttpContext.User = user;
}
}
private IPrincipal GetUserFromToken(string token)
{
// Here you could put your custom logic to decrypt the token and
// extract the associated user from it
throw new NotImplementedException();
}
private void HandleUnAuthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/CustomError.cshtml",
};
}
}
and then you could decorate your action with this attribute:
[TokenAuthorize]
public ActionResult ProcessEmail(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
I just wanted to know how to return a single entity from a wcf data service?
I want to provide a methode public User Login(string username, string password) and return the loggedin user to the user interface.
Thanks!
andi
Old question is old, but you'll want to define a Service Operation:
[WebGet]
[SingleResult]
public User GetAuthenticatedUser(string username, string password) {
//
// Fetch and return the user
// with the given parameters
//
}
However the username and password are passed in clear text via the URI. A better option would be pulling the username and password data from the Authorization header of the HTTP request, encrypting the request over the wire (SSL). A simple example could be defining a constructor for the Data Service, and in it attaching to the ProcessingRequest event:
public MyDataService() {
this.ProcessingPipeline.ProcessingRequest += (o, e) => {
//
// Do stuff with e.OperationContext.RequestHeaders["Authorization"]
// storing the result into an instance property
//
};
}
With a User GetCurrentUser() service operation that references the authorization property for the username and password, instead of accepting them as GET querystring parameters.
I think this will help you.
I assume User is your entity table.
public User Login(string username,string password)
{
Entity obj = new Entity();
User objUser = (from U in obj.User where U.username = username and U.password = password Select U).Single();
return objUser;
}
This works fine for me I hope it helps you.
I would like to create some sort of authentication attribute and attach it to various OperationContracts. Inside this attribute, it would check for an authentication token and make sure its still valid before the OperationContract is run.
What's the best way to implement this on the .net platform? Does wcf have any special attributes that already do this type of functionality? What I'm picturing is something similar to the attributes you can attach to MVC controllers that will perform operations before actions are run.
In case it's relevant, I am using WCF to create SOAP web services that will be consumed by clients on various platforms that support SOAP.. not just WCF clients
Here's some code to clarify what I'm trying to do:
interface:
[ServiceContract]
public interface IService
{
[OperationContract]
string ValidateUser(string username, string password);
[OperationContract]
string GetDataAndAuthInCode(string authtoken);
[MyAuthorizationAttribute]
[OperationContract]
string GetDataAndAuthWithAttribute(string authtoken);
}
implementation:
public class Service : IService
{
public string ValidateUser(string username, string password)
{
if (!Membership.ValidateUser(username, password))
throw new Exception("invalid user...");
else
return GenerateAuthToken(username);
}
public string GetDataAndAuthInCode(string authtoken)
{
if (!IsAuthTokenValid(authtoken))
throw new Exception("Auth token invalid expired");
else
return GetData();
}
public string GetDataAndAuthWithAttribute(string authtoken)
{
return GetData();
}
}
Looks like this is what I'm looking for.. "Custom Behaviors":
http://msdn.microsoft.com/en-us/magazine/cc163302.aspx#S7