IdentityServer WithCustomUserService on External Login throws nullreference exception - identityserver3

I Implementd a custom userservice to store user data in a database.
Since that I get a NullReferenceException when I try to authenticace with an external provider like facebook.
I can see this stack in the exception which indicates that a value is missing for the loginpage. As A result after clicking on the facebook button i'm standing again at the starting login page.
But I don't know why or which value exactly
I can see that at the end of AuthenticateExternal context.AuthenticateResult.User.Claims contains these claims
my user service looks simplified like this
public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
{
string id = context.ExternalIdentity.Claims.FirstOrDefault(i => i.Type == "id").Value;
var user = await gateway.ByExternalIds(context.ExternalIdentity.Provider, id);
if (user == null)
{
string displayName = context.ExternalIdentity.Claims.FirstOrDefault(i => i.Type.Equals("urn:facebook:name")).Value;
user = new User(context.ExternalIdentity);
await gateway.StoreAsync(user);
}
if (user != null)
{
await gateway.SetLastLogin(user.Subject, DateTimeOffset.Now);
context.AuthenticateResult = new AuthenticateResult(user.Subject, GetDisplayName(user), identityProvider: context.ExternalIdentity.Provider);
}
}
What am I missing?

Found it. This behavior occurs when the method for detemerning if the user ist active returns false.
Task IsActiveAsync(IsActiveContext context)
its little unexpected as I had expected that in this case i would see something like "unknown user" or "inactive user"

Related

Preventing multiple spurious calls to backend in Flutter

I have a service class in Flutter that fetches objects from the server using Rest API calls. It caches those objects and returns cached objects if the UI tries to fetch the same object again. Like this:
Future<User?> getUser(int userid) async{
User? u = _userCache.get(userid);
if(u == null) {
ApiResponse<User> ar = await _restapi.getUser(userid);
if (ar.successful && ar.data != null) {
u = ar.data!;
_userCache.add(userid,u);
}
}
return u;
}
Now, the problem that I am having is as follows: The UI requires the same User object multiple times while building a screen and so, it invokes getUser(id) multiple times. Ideally, it should return the cached User for the second and subsequent requests but the first request to the server hasn't completed yet and the cache does not have the User object (even though it will have the object a few seconds later). This causes the app to invoke the Rest API calls multiple times.
I want to basically have just one active/pending call to the RestAPI. Something like this:
Future<User?> getUser(int userid) async{
>>>> CHECK whether a call to backend is pending. If so, then wait here.<<<<
User? u = _userCache.get(userid); <--This call should now succeed
...same as before
}
Is this how it should be done? What is the right way to achieve this?
====== UPDATE =================
Based on #dangngocduc suggestion my getUser method now looks like this:
Future<User?> getUser(int userid) async{
User? u = _userCache.get(userid);
if(u == null) {
await userLock.synchronized( () async {
u = _userCache.get(userid);
if(u == null) {
ApiResponse<User> ar = await _uome.getUser(userid);
if (ar.successful && ar.data != null) {
u = ar.data!;
_userCache.add(userid, u!);
}
}});
}
return u;
}
Your issue is on first time, when we don't have cache and have multiple request for a user.
I think you can try with this package:
https://pub.dev/packages/synchronized
Let add your logic on synchronized.

Keycloak - template for handling an already logged in user

Using keycloak 11 I tried to find the page for the already logged in user.
I want to customize this page but I fail to find it.
I looked into the message to find the message, and I did its called alreadyLoggedIn.
I tried looking into each template belonging to the base/login folder but I still didn't find the variable. So I supposed its set somewhere in the code but I didn't find anything in the doc or the forums.
anyone please provide the template name and/or its location in the keycloak repo ?
The page you are looking for is template.ftl. The content of alreadyLoggedIn is in h1#kc-page-title and set by a nested header.
The info.ftl page is the one being used for ALREADY_LOGGED_IN message. The template.ftl is never directly used for showing any page from what I know.
You can see from the source code
/**
* Verifies that the authentication session has not yet been converted to user session, in other words
* that the user has not yet completed authentication and logged in.
*/
public static <T extends JsonWebToken> void checkNotLoggedInYet(ActionTokenContext<T> context, AuthenticationSessionModel authSessionFromCookie, String authSessionId) throws VerificationException {
if (authSessionId == null) {
return;
}
UserSessionModel userSession = context.getSession().sessions().getUserSession(context.getRealm(), authSessionId);
boolean hasNoRequiredActions =
(userSession == null || userSession.getUser().getRequiredActionsStream().count() == 0)
&&
(authSessionFromCookie == null || authSessionFromCookie.getRequiredActions() == null || authSessionFromCookie.getRequiredActions().isEmpty());
if (userSession != null && hasNoRequiredActions) {
LoginFormsProvider loginForm = context.getSession().getProvider(LoginFormsProvider.class).setAuthenticationSession(context.getAuthenticationSession())
.setSuccess(Messages.ALREADY_LOGGED_IN);
if (context.getSession().getContext().getClient() == null) {
loginForm.setAttribute(Constants.SKIP_LINK, true);
}
throw new LoginActionsServiceException(loginForm.createInfoPage());
// ^^ createInfoPage is being called
}
}

User Password Not Resetting in ASP Core

Im attempting to create a forgot password feature in asp core with entity framework.
[UnitOfWork]
public virtual async Task PasswordReset(PasswordResetViewModel resetPasswordViewModel)
{
//var user = await GetUserByChecking(emailAddress);
var user = await _userManager.FindByEmailAsync(resetPasswordViewModel.UsernameOrEmailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
var result = await _userManager.ResetPasswordAsync(user, resetPasswordViewModel.PasswordResetToken, resetPasswordViewModel.NewPassword);
switch (result.Succeeded)
{
case true:
throw new UserFriendlyException("Password Reset");
case false:
throw new UserFriendlyException(result.Errors.ToString());
default:
break;
}
}
when the following runs
userManager.ResetPasswordAsync
i get a successful result however i am still unable to login with the new password and the old password continues to work.
I have fixed this. The issue was that I returned an exception on a successful password reset. This for some reason did not save the change . I changed it to return a JSON string and it worked

Seam framework storing active user in session scobe

I want to store activeUser object in session scobe when someone logged in, I am using it forward for user specific operations but there is a problem accours when admin attemps to loggin because admin is not a default user so #out annotation causes an error
error code is familiar = #Out attribute requires non-null value: authenticator.activeUser
my logic code is below;
(note: admin and standart user use same login panel)
#Out(scope = ScopeType.SESSION, required = true)
private User activeUser;
public boolean authenticate() throws Exception {
// SELECT OBJECT(user) FROM User user WHERE user.name = :name and
// user.password = :pass
try {
// create a query for getting a user with specified parameters if no
// user exist throw an exception
setActiveUser((User) entityManager
.createQuery(
"select u from User u where u.userName= :uname and u.password = :pass")
.setParameter("uname", credentials.getUsername())
.setParameter("pass", credentials.getPassword())
.getSingleResult());
identity.addRole("user");
return true;
} catch (Exception e) {
// so there is no any normal user now looking for any admin entry
System.out.println("there is no any member looking for admin");
try {
Admin admin = (Admin) entityManager
.createQuery(
"select a from Admin a where a.username = :uname and a.pass = :p")
.setParameter("uname", credentials.getUsername())
.setParameter("p", credentials.getPassword())
.getSingleResult();
identity.addRole("admin");
System.err.println("found admin");
return true;
} catch (Exception e2) {
System.err.println("admin bulunamadı");
throw e2;
}
}
}
in #out required must be true because in user specific crud operations I am logging them with user id.
how can I solve this problem. setting required=false is enough?
and the other hand if no user finds the entitymanager.resultlist() method throws an exception. so i know that the #out annotataions works unless the methods throw any excepttion?

Specflow with MVC Model Validation Issue

I am using Specflow, nunit and moq to test the default MVC2 application registration as I learn SpecFlow.
I have the following steps for checking if the username and password have not been entered.
Steps
[Given(#"The user has not entered the username")]
public void GivenTheUserHasNotEnteredTheUsername()
{
_registerModel = new RegisterModel
{
UserName = null,
Email = "test#dummy.com",
Password = "test123",
ConfirmPassword = "test123"
};
}
[Given(#"The user has not entered the password")]
public void GivenTheUserHasNotEnteredThePassword()
{
_registerModel = new RegisterModel
{
UserName = "user" + new Random(1000).NextDouble().ToString(),
Email = "test#dummy.com",
Password = string.Empty,
ConfirmPassword = "test123"
};
}
[When(#"He Clicks on Register button")]
public void WhenHeClicksOnRegisterButton ()
{
_controller.ValidateModel(_registerModel);
_result = _controller.Register(_registerModel);
}
[Then(#"He should be shown the error message ""(.*)"" ""(.*)""")]
public void ThenHeShouldBeShownTheErrorMessage(string errorMessage, string field)
{
Assert.IsInstanceOf<ViewResult>(_result);
var view = _result as ViewResult;
Assert.IsNotNull(view);
Assert.IsFalse(_controller.ModelState.IsValid);
Assert.IsFalse(view.ViewData.ModelState.IsValidField(field));
Assert.IsTrue(_controller.ViewData.ModelState.ContainsKey(field));
Assert.AreEqual(errorMessage,
_controller.ModelState[field].Errors[0].ErrorMessage);
}
Extension method to force validation
public static class Extensions
{
public static void ValidateModel<T> ( this Controller controller, T modelObject )
{
if (controller.ControllerContext == null)
controller.ControllerContext = new ControllerContext();
Type type = controller.GetType();
MethodInfo tryValidateModelMethod =
type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(
mi => mi.Name == "TryValidateModel" && mi.GetParameters().Count() == 1).First();
tryValidateModelMethod.Invoke(controller, new object[] { modelObject });
}
}`
I do not understand why the password missing test fails on the following lines.
Assert.IsFalse(view.ViewData.ModelState.IsValidField(field));
Assert.IsTrue(_controller.ViewData.ModelState.ContainsKey(field));
I have noticed that the error message being returned is for the Password and ConfirmPassword not matching but I dont understand why for all the other tests, including the Missing Confirm Password test (Identical to the missing Password test) they work fine.
Any ideas?
Features
Scenario: Register should return error if username is missing
Given The user has not entered the username
When He Clicks on Register button
Then He should be shown the error
message "The Username field is required." "username"
Scenario: Register should return error if password is missing
Given The user has not entered the
password
When He Clicks on Register button
Then He should be shown the error message "'Password' must be at least
6 characters long." "Password"
UPDATE
Ok seems the ValidatePasswordLengthAttribute in the Account Model couldn't initilise Membership.Provider as I did not have the connectionstring in my app.config. Is the Pembership.Provider connecting to the membership DB now?
I have added the connection string but now the test passes 50% of the time as it returns two errors:
Password required
Password must be 6 chars long.
The problem is that they are not returned in the same order every time so the test is flaky.
How can I rewrite my scenario and test to account for this? Can I still keep the one "Then" method or do I need to create a new method?
Thanks.
I had to add the connection string the the AccountService to the App.config which nunit uses. This was causing an error on the ValidatePasswordLengthAttribure.
I have updated the Assert which checks for the correct error message to:
Assert.AreEqual(errorMessage,
_controller.ModelState[field].Errors.First(e => e.ErrorMessage == errorMessage).ErrorMessage);
Still unsure about whether the Membership.Provider is hitting the DB