custom Shiro realm - who does the actual authentication - shiro

when sub classing shiro's AuthorizingRealm (or only AuthenticationRealm) by overriding
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
}
Is it my job to check that the credentials provided in the AuthenticationToken actually match?
Or am I supposed to return the AuthenticationInfo with the principals resolved from the AuthenticationToken and the correct password for the given credentials and shiro will compare them on its own somewhere within the flow of the Subject.login(AuthenticationToken) call?

The Javadocs for AuthenticatingRealm.doGetAuthenticationInfo() state (emphasis mine):
Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given authentication token.
For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing more and letting Shiro do the rest. But in some systems, this method could actually perform EIS specific log-in logic in addition to just retrieving data - it is up to the Realm implementation.
The method AuthenticatingRealm.getAuthenticationInfo() first calls doGetAuthenticationInfo() then subsequently calls assertCredentialsMatch() using the configured credentialsMatcher:
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
So depending on how typical your Realm implementation is, you might want to avoid checking the AuthenticationToken's credentials in the doGetAuthenticationInfo() method, because the getAuthenticationInfo() template method already contains a step to ensure the submitted credentials match.
To specifically address your question if it is your responsibility "to check that the credentials provided in the AuthenticationToken actually match", the answer is yes, but not in the doGetAuthenticationInfo() method. Typically you would perform the credentials comparison within an implementation of the CredentialsMatcher interface, as described here.

Inside doGetAuthenticationInfo(...) you need to verify that the user has provided you with authentication proof.
Here is a pseudo-coded example of what you might do:
protected AuthenticationInfo doGetAutheticationInfo(AuthenticationToken token) {
if(token instanceof UsernamePasswordToken) {
String username = token.getUsername();
// Look up the user by the provide username
UserRecord userRecord = lookupUserRecord(username);
// No record found - don't know who this is
if (userRecord == null) {
throw new UnknownAccountException();
}
// Check for other things, like a locked account, expired password, etc.
// Verify the user
SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(userRecord.getPrincipal(), userRecord.getHashedCredentials(), userRecord.getCredentialsSalt(), getName());
boolean successfulAuthentication = getCredentialsMatcher().doCredentialsMatch(token, sai);
if(successfulAuthentication) {
// Check for anything else that might prevent login (expired password, locked account, etc
if (other problems) {
throw new CredentialsException(); // Or something more specific
}
// Success!
return sai;
} else {
// Bad password
throw new IncorrectCredentialsException();
}
}
// Don't know what to do with this token
throw new CredentialsException();
}
You'll have to write lookupUserRecord(username) or something similar to go lookup the user information including his hashed and salted credentials.

doGetAuthenticationInfo is the main method where authentication is done. SO if you override it generally you are overriding authentication process. If you want to use the process that was defined for that reealm and do some extra things better call super class method first then get its info and then use it so you will not have to change anything. Also in case of jdbcrealm sqls in shiro.ini are automatically mapped. and they will not be changed until you override
setAuthenticationQuery, setUserRolesQuery, etc
You can easily call following method to simulate the actual process then customize it.
AuthenticationInfo info = super.doGetAuthenticationInfo(token);
Note, that super is a reference to the parent, but super() is it's constructor.
like:
public class CustomJdbcRealm extends JdbcRealm
{
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
AuthenticationInfo info = super.doGetAuthenticationInfo(token);
// Your own code here
}
}

Related

Handle errors for federated users password update in keycloak

I am using keycloak custom user storage provider and users are getting from an external service.
The class implements the following interfaces:
public class CustomUserStorageProvider implements UserStorageProvider, UserLookupProvider, CredentialInputValidator, UserQueryProvider, CredentialInputUpdater {
When the user password is expired in isValid method the exception is catched and the requiredAction is added to prompt user to set a new password
UserModel userAdapter = loadedUsers.get(user.getUsername());
if (userAdapter != null) {
userAdapter.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
return true;
}
The updateCredential method sends a request to an external service to update user password
#Override
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
try {
updateUserPassword(user.getUsername(), input.getChallengeResponse());
} catch (Exception e) {
log.error("Something went wrong", e);
}
return true;
}
This works normally, updates the user password in an external service and authenticates user.
I need to display the according error message coming from an external service when something went wrong during the updating password e.g it doesn't meet the needs of the password policy(this is also defined in external service rather than in keycloak).
I researched but couldn't find a way how to do that.
Is there any way to achieve that?

How to storage and get token when custom ClientDetailsService?

This is my code.
The clientService implements ClientDetailsService , but not found the token storage.
How to storage and get token when custom(e.g. database) ClientDetailsService?
When we have a custom implementation of ClientDetailsService, we essentially override its loadClientByClientId(..) method. This method takes clientId in parameter i.e. the username of the client. In that custom implementation class, all we need to do is check if the given client name exists in database or not. If it does exist, then load all its data and return the object. This class needs be injected with DAO or Repository's dependency to talk to database.
#Override
public ClientDetails loadClientByClientId(final String clientId) throws ClientRegistrationException {
Objects.requireNonNull(clientId, "Client ID must not be null");
final com.ex.auth.domain.ClientDetails clientDetails = clientDetailsRepository.findOne(clientId);
if (clientDetails == null) {
throw new NoSuchClientException(String.format("Client %s does not exist.", clientId));
}
return convertToDmo(clientDetails);
}

Issues with CurrentUserPropertyBinder it cannot always remember user

I have implemented a CurrentUserPropertyBinder (see below) for a web application using FubuMVC.
public class CurrentUserPropertyBinder : IPropertyBinder
{
private readonly Database _database;
private readonly ISecurityContext _security;
public CurrentUserPropertyBinder(Database database, ISecurityContext security)
{
_database = database;
_security = security;
}
public bool Matches(PropertyInfo property)
{
return property.PropertyType == typeof(User)
&& property.Name == "CurrentUser";
}
public void Bind(PropertyInfo property, IBindingContext context)
{
var currentUser = //check database passing the username to get further user details using _security.CurrentIdentity.Name
property.SetValue(context.Object, currentUser, null);
}
}
When I login to my site, this works fine. The CurrentUserPropertyBinder has all the information it requires to perform the task (i.e. _security.CurrentIdentity.Name has the correct User details in it)
When I try and import a file using fineUploader (http://fineuploader.com/) which opens the standard fileDialog the _security.CurrentIdentity.Name is empty.
It doesn't seem to remember who the user was, I have no idea why. It works for all my other routes but then I import a file it will not remember the user.
Please help! Thanks in Advance
NOTE: We are using FubuMVC.Authentication to authenticate the users
I'm guessing your action for this is excluded from authentication; perhaps it's an AJAX-only endpoint/action. Without seeing what that action looks like, I think you can get away with a simple fix for this, if you've updated FubuMVC.Authentication in the past 3 months or so.
You need to enable pass-through authentication for this action. Out of the box, FubuMVC.Auth only wires up the IPrincipal for actions that require authentication. If you want access to that information from other actions, you have to enable the pass-through filter. Here are some quick ways to do that.
Adorn your endpoint/controller class, this specific action method, or the input model for this action with the [PassThroughAuthentication] attribute to opt-in to pass-through auth.
[PassThroughAuthentication]
public AjaxContinuation post_upload_file(UploadInputModel input) { ... }
or
[PassThroughAuthentication]
public class UploadInputModel { ... }
Alter the AuthenticationSettings to match the action call for pass-through in your FubuRegistry during bootstrap.
...
AlterSettings<AuthenticationSettings>(x => {
// Persistent cookie lasts 3 days ("remember me").
x.ExpireInMinutes = 4320;
// Many ways to filter here.
x.PassThroughChains.InputTypeIs<UploadInputModel>();
});
Check /_fubu/endpoints to ensure that the chain with your action call has the pass-through or authentication filter applied.

How to authenticate and redirect a user to his 'own' page in Jersey REST service

How to authenticate and redirect a user to his own page i.e to www.mysite.com/"user's email".
I am using the following algo which is not working...
userDB in User class:
Map<String,String> userdata=new HashMap<String,String>();
First my login process form :
#Path("/login")
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void login(
#FormParam("email") String emailc,
#FormParam("password") String pass,
#Context HttpServletResponse servletResponse
) throws IOException,RuntimeException {
User u1=new User();
pass=u1.getPassword();
emailc=u1.getEmailaddrs();
boolean checked=false;
boolean exists;
exists=u1.userdata.containsKey(emailc);
if(exists){
String mypass =u1.userdata.get(emailc);
if(mypass==pass){
checked=true;
}else{
checked=false;
}
}else{
checked=false;
}
if(!checked){
//User Doesn't exists
servletResponse.sendRedirect("http://localhost:8080/MySite/pages/Create_Profile.html");
}else{
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
}
}
createprofile
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newUser(
#FormParam("email") String email,
#FormParam("password") String password,
#Context HttpServletResponse servletResponse
) throws IOException {
User u = new User(email,password);
User.userdata.put(email,password);
}
Your usage of userdata [Map] looks wrong to me. Is it a part of user class, is it non static or static ?
If it is non static then every time you will do new User() .. that map will be initialized and it will have no data in it. Hence u1.userdata.containsKey(emailc); will be always false.
If you are using a hashmap as a temporary database for dev purposes then, make it static rather keep it in a different class like UserStore or some DB access layer. Exmaple below:
public class UserDAO(){
private static Map<String,User> userdata = new HashMap<String,User>();
public boolean hasUser(String email){
return userdata.contains(email);
}
public User saveUser(String email, String password ...){
//make user object save it in map and return the same
}
// more methods for delete and edit etc.
}
And use this in your REST layer classes like this
exists = userDao.hasUser(email);
Advantages :
Your problem will be solved.
Later on when you move to actual db implementation you will just have to change your UserDao code and rest application code will be just fine. -- Loose coupling :)
Also regarding forward using email
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
add the email parameter there in the url only, if thats what you want:
servletResponse.sendRedirect("http://localhost:8080/MySite/"+emailc);
UPDATE :
See the fundamental thing is that you get request parameters [email , password]. You check it whether it is present in map or not. Now what you are doing wrong here is you create a new user like this User u = new User(); and then get email and password from it emailc = u.getEmail();. This emailc will always be null and your userdata map will always return false for that. You have two choices :
Either set email and password in user object and then get the data from user object.
Use the email and password obtained from request parameters for your logic. Do not alter them
One good practice to follow while programming is that at all times think of your method parameters as final parameters.
UPDATE 2 :
if(mypass==pass){
checked=true;
}else{
checked=false;
}
Change == to equals method. String matching should be done by equals or equalsIgnoreCase method not ==.
You always create a new User without any parameters: User u1=new User();. All these User instances will have the same property values and probably exists is always false.

Is this the right way to do stateless authentication per call on ServiceStack?

I have REST service requirements in which some calls require authentication and some don't. Absolutely no state is used, as the calls are all independent from one another. I have put something together which seems to work, but is this the right way to go about not using sessions?
This question is kind of related to my WCF question which is answered here.
Firstly I registered the authentication method:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}
));
I then attribute the respective calls (or service or DTO) with the Authenticate attribute:
[Authenticate]
public HelloResponse Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name + " with POST & Auth"};
}
I inherit from the BasicAuthProvider class which does the authentication:
public class CustomCredentialsAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return userName == "dylan" && password == "abc123";
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
session.IsAuthenticated = true;
//Important: You need to save the session!
authService.SaveSession(session, new TimeSpan(0,0,10));
}
}
As you can see, I do save the session but it times out after 10 seconds. This is the part that I'm sure can potentially be done better. It seems to work nicely though.
Is there a better way of doing what I'm trying to accomplish?
Is there also any way, due to the sessionless nature of these services, to remove the Auth, AssignRoles and UnassignRoles methods?
If you wanted to keep using ServiceStack's Authentication and Session support you could just add a response filter that clears the user session after the service is executed, e.g:
this.ResponseFilters.Add((req, res, dto) => req.RemoveSession());
Basically after each request is executed it clears the session, so no record of them having authenticated exists.
Otherwise you can just skip using ServiceStack's Authentication completely and just provide your own via RequestFitlers of FilterAttributes (which is essentially what SS Auth does under the hood).