How to provide user login with a username and NOT an email? - firebase-authentication

I'd like to use Firebase for my web app that is for people with dementia in a care home. They do not have email or social network accounts so will need a simple username / password sign up / sign in.
What is the easiest way to do this? From what I can see in the docs I'd have to use a custom auth flow but I do not have an existing auth server.
If I do need ot do this what is the easiest way to provide the token? In Azure there is Functions and AWS has Lambda but I see nothing here is Firebase

You are correct that username/password sign-in is not supported natively in Firebase Auth at this moment.
You can implement a custom provider as shown in this example. This allows you to meet any custom requirements, but is admittedly a bit more involved than using the built-in providers. There is an example of this here that you can use as a starting point.
A workaround you could take without needing to use custom auth with another backend is to accept usernames in your UI, but on the underlying logic, append "#yourowndomain.com" before calling the functions to sign up or sign in with email.
So you would be using email/password authentication, mapping <username> to <username>#yourowndomain.com

You can use sign in with custom token
Firebase gives you complete control over authentication by allowing you to authenticate users or devices using secure JSON Web Tokens (JWTs). You generate these tokens on your server, pass them back to a client device, and then use them to authenticate via the signInWithCustomToken() method.
You need to save username and password in your database or rtdb or firestore
When user touch the login button, client will send username and password to your backend. If the username and password correct, generate custom token and send it back to the client
Client then can login with custom token from the server using signInWithCustomToken() method
More detail can be read in this documentation

Appending a dummy domain at end is a kind of a patch up and should be avoided.
To enable username login just follow these simple steps.
Sign Up
During sign up take the userid , email and password . Register the user with normal email and password. On Success of it save the email against the user_id in a separate node(branch).
mButtonSignUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(isValid()){
mProgressBar.setVisibility(View.VISIBLE);
final String userId = mEditTextUserId.getText().toString();
final String emailId = mEditTextEmail.getText().toString() ;
String password = mEditTextPassword.getText().toString() ;
firebaseRef.createUser(emailId, password, new Firebase.ResultHandler() {
#Override
public void onSuccess() {
firebaseRef.child("user_ids").child(userId).setValue(emailId);
Toast.makeText(getBaseContext(),"You are successfully registered ",Toast.LENGTH_SHORT).show();
mProgressBar.setVisibility(View.GONE);
}
#Override
public void onError(FirebaseError firebaseError) {
mProgressBar.setVisibility(View.GONE);
Toast.makeText(getBaseContext(),firebaseError.toString(),Toast.LENGTH_SHORT).show();
}
});
}
}
});
Database
Database structure will look like this
Login
Check if the user has entered an email or userId. If it is a email id then directly perform login with it otherwise fetch the email id associated with the username and perform login.
Button buttonLogIn = (Button)findViewById(R.id.button_login);
buttonLogIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mProgressBar.setVisibility(View.VISIBLE);
String username = mEditTextEmail.getText().toString() ;
final String password = mEditTextPassWord.getText().toString() ;
// Check if it is an email or not
if(android.util.Patterns.EMAIL_ADDRESS.matcher(username).matches()) {
performLogin(username,password);
}else{
//get the emailId associated with the username
firebaseRef.child("user_ids").child(username)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot!=null){
String userEmail = dataSnapshot.getValue(String.class);
performLogin(userEmail,password);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
//Handle Error
}
});
}
}
});
private void performLogin(String emailId, String password) {
firebaseRef.authWithPassword(emailId,password, new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
uid = authData.getUid() ;
Toast.makeText(getBaseContext(), authData.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
Toast.makeText(getBaseContext(), firebaseError.toString(), Toast.LENGTH_SHORT).show();
}
});
}

You may use Alfonso's solution as well. And where you need a real e-mail, you can set an textfield for an e-mail when the user registers and you can keep it in your database and you can use it.

I couldn't find support by firebase for this.
However, doing the following will solve your problem.
SIGN UP
private void clickListener() {
registerbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String email = emailEdit.getText().toString();
String pass = passwordEdit.getText().toString();
if (email.isEmpty()) {
emailEdit.setError("Lutfen Kullanici Adinizi Giriniz.");
return;
}
if (pass.isEmpty()) {
passwordEdit.setError("Lütfen Parolanizi Giriniz.");
return;
}
email=email+"#gmail.com";
createAccount(email,pass);
}
});
}
private void createAccount(String email, String pass){
auth.createUserWithEmailAndPassword(email, pass)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
FirebaseUser user=auth.getCurrentUser();
updateUi(user,pass);
}
else{
Toast.makeText(MainActivity.this, "Error: "+
task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
This way we add the phrase "#gmail.com" to the email. email=email+"#gmail.com";
Let the value we take as an example be "abdulkerim". Even if the user enters the phrase "abdulkerim", we get a username instead of an email, thanks to the "#gmail.com" we added to the end.
database view

Related

Spring security - new access token

I have following issue.
I have multitenant system (with shared Database and shared Schema). Access token that is generated when user logs in contains information about tenantId. Idea is to allow logged user to change tenat
For example: My user works for 3 tenants(hospitals). When he is logged in, he should be able to change hospital.
So the main issues is how to generate new acces token for user that will contain updated tenantId.
It would be preferable that user doesnt have to provide password again (since he is already logged in), and that request to auth-server that he triggers would contain his current token (that will confirm that he is currently authenticated) and newTenandId.
Here is some custom code:
#Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private MessageSource validationMessageSource;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
SpringSecurityUserWithAdditionalData user = (SpringSecurityUserWithAdditionalData) userDetailsService.loadUserByUsername(username);
return checkPassword(user, password);
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private Authentication checkPassword(SpringSecurityUserWithAdditionalData user, String rawPassword) throws AuthenticationException {
try {
if (passwordEncoder.matches(rawPassword, user.getPassword())) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
return token;
} else {
throw new GeneralException(validationMessageSource.getMessage("security.authentication.NotValid", new Object[] {}, LocaleContextHolder.getLocaleContext().getLocale()));
}
} catch (Exception e) {
throw new BadCredentialsException(e.getMessage());
}
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration {
#Autowired
private CustomAuthenticationProvider authenticationProvider;
// #formatter:off
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
...
.and()
.logout()
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.and()
.formLogin()
.loginPage("/login")
.loginPage("/changeTenant")
.permitAll().and();
return http.build();
}
// #formatter:on
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
/**
* JWT koji je generisao authorization server sadrzi granted permissions (Spring ih naziva granted authorities) u okviru "scope" claim-a.
* Umesto njega cemo koristiti custom claim koji sam nazvao GlobalConstants.JWT_CLAIM_ROLA_LIST za specifikaciju rola koje ima authenticated korisnik.
* Spring koristi default instance JwtAuthenticationConverter koja ocekuje granted authorities u okviru "scope"/"scp" claim-a.
* Da bi koristili umesto standardno "scope" claim-a koristili claim GlobalConstants.JWT_CLAIM_ROLA_LIST override-ovan je JwtAuthenticationConverter.
*/
#Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName(GlobalConstants.JWT_CLAIM_ROLA_LIST); // override authorities claim-a
converter.setAuthorityPrefix(""); // eksplicitno definisemo nazive, bez podrazumevanih prefiksa (ROLE_ SCOPE_ i slicno)
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
#Bean
InitializingBean forcePostProcessor(BeanPostProcessor meterRegistryPostProcessor, MeterRegistry registry) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
}
}
If you need any additional information, please say.
I tried adding custom fields to custom login form, that will have hidden tenantId field. But i could not manage to make it work.
The authentication process should be designed to return a list of all the tenants the user has access to, typically as a list of authorities.
Separately you need a back-end call (linked to the UI) that allows the user to choose the current tenant from the list of authorities returned in authn.
The value of the current tenant must be stored in the session.
If you really want to hack this via the auth roles, you could store the real auth token and generate your own token with only the current tenant. When user changes tenant they get a new token with the new tenant (obviously after checking against the real token)

Email Invitation for complete registration using asp.net Identity

I'm trying to implement user invitation using asp.net Identity in my application, the users need to be invited by an Admin rather than start the registration themselves. So in my invitation process, I create a user without a password, adding the correct role and send an email with GenerateUserTokenAsync. So far so good. (open for suggestion tho)
My concerns are:
After the user clicks in the link he/she received by email, it goes to a View I created, but I wonder if it would be better to re-use one of the Identity Pages, Confirme Email Registration for example, or the registration itself.
Any thoughts?
According to your description, if you want to reuse Email confirm page, you could try to use identity confirm page razor page as below. Besides, I suggest you could try to redirect the user to change passwork page to change the password.
You need to generate the Email link with two querystring parameters, userId and code. More details, you could refer to below codes:
[AllowAnonymous]
public class ConfirmEmailModel : PageModel
{
private readonly UserManager<IdentityUser> _userManager;
public ConfirmEmailModel(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGetAsync(string userId, string code)
{
if (userId == null || code == null)
{
return RedirectToPage("/Index");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return NotFound($"Unable to load user with ID '{userId}'.");
}
code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
var result = await _userManager.ConfirmEmailAsync(user, code);
StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email.";
return Page();
}
}

JBoss multiple authentication

Hello everyone,
I need to get authenticated in my JBoss AS 7 by using different ways. The app is using form-based authentication, I need to implement another way to do it but without login page, maybe by using token, certification, etc... I do not know how to do it, but the authentication needs to be performed without login.
Is there a way in Jboss?
Thanks,
Luis.
hi what are the security requirements for this other login method? you could use a certificate based authentication. all the different login method are listed here: https://docs.jboss.org/author/display/WFLY8/Authentication+Modules
If you need to create your own Login module you can follow examples such as this here https://github.com/radcortez/wildfly-custom-login-module:
public class CustomLoginModule extends UsersRolesLoginModule {
private CustomPrincipal principal;
#Override
public boolean login() throws LoginException {
boolean login = super.login();
if (login) {
principal = new CustomPrincipal(getUsername(), "An user description!");
}
return login;
}
#Override
protected Principal getIdentity() {
return principal != null ? principal : super.getIdentity();
}
}
public class CustomPrincipal extends SimplePrincipal {
private String description;
public CustomPrincipal(String name, String description) {
super(name);
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Basically use the classes from org.jboss.security
does that help?
cheers
oliver

How can I populate a UserAuth from values in Redis?

This is my custom user authentication setup in my global.asax file, but I am currently providing the users manually in the Configure method; Is it possible to take values from a Redis server?
For example if user exists and the password is okay, can fill with these details automatically?
Plugins.Add(new AuthFeature(()=>
new AuthUserSession(),
new IAuthProvider[]{ new BasicAuthProvider() }
));
container.Register<ICacheClient>(new MemoryCacheClient());
var userRepo = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRepo);
string hash, salt;
new SaltedHash().GetHashAndSaltString("password", out hash, out salt);
userRepo.CreateUserAuth(new UserAuth
{
Id = 1,
DisplayName = "Haluk",
Email = "hal",
UserName = "haluk",
FirstName = "haluk",
LastName = "yılmaz",
PasswordHash = hash,
Salt = salt
}, "password");
Yes you can authenticate against a Redis data source. You can either use the built in RedisAuthRepository in place of the InMemoryAuthRepository, or if you have an existing Redis data set that you want to use instead of the built-in IAuthRepository pattern, I have included a solution for that, whereby you extend the BasicAuthProvider. The first method is the most straightforward:
Use the RedisAuthRepository:
So you need to establish a connection to Redis.
Then register your authentication providers.
Register the RedisAuthRepository, which the authentication providers will check credentials against, and is compatible with the RegistrationFeature
private IRedisClientsManager redisClientsManager;
public override void Configure(Funq.Container container)
{
// Configure ServiceStack to connect to Redis
// Replace with your connection details
redisClientsManager = new PooledRedisClientManager("127.0.0.1:6379");
container.Register<IRedisClientsManager>(c => redisClientsManager);
container.Register<ICacheClient>(c => c.Resolve<IRedisClientsManager>().GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);
// Setup the authorisation feature
Plugins.Add(new AuthFeature(()=>
new AuthUserSession(),
new IAuthProvider[]{ new BasicAuthProvider() }
));
// Use a RedisAuthRepository
var userRepo = new RedisAuthRepository(redisClientsManager);
container.Register<IUserAuthRepository>(userRepo);
// You can then register users as required using the RegistrationFeature
}
Alternatively (if you have an existing user authentication dataset in Redis)
You can do this by creating a custom authentication provider that extends the existing BasicAuthProvider.
For this code you should also make sure that your familiar with the ServiceStack.Redis client.
Extend the BasicAuthProvider:
This MyRedisBasicAuthProvider extends the existing BasicAuthProvider, and instead of performing the credentials lookup from an IUserAuthRepository as given in your example code, it makes a Redis connection and matches the username to entry in Redis.
The code is fully commented but if there is anything you wish further explained, let me know.
public class MyRedisBasicAuthProvider : BasicAuthProvider
{
// The key at which we will store the user profile. i.e user:john.smith or user:homer.simpson
// Replace this key with your format as required
public const string UserKeyFormat = "user:{0}";
MyUser CurrentUser;
// Gets an instance of a redis client
static IRedisClient GetRedisClient()
{
// Get the RedisClientsManager from the Container
var redisClientManager = HostContext.TryResolve<IRedisClientsManager>();
if(redisClientManager == null)
throw new Exception("Redis is not configured");
// Return a client
return redisClientManager.GetClient();
}
// This method is used to verify the credentials provided
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// Get a Redis client connection
using(var redisClient = GetRedisClient())
{
// Get a typed Redis Client
var userClient = redisClient.As<MyUser>();
// Try to find a matching user in Redis
CurrentUser = userClient.GetValue(string.Format(UserKeyFormat, userName));
// Check the user exists & their password is correct (You should use a hashed password here)
return CurrentUser != null && password == CurrentUser.Password;
}
}
// This method is used to populate the session details from the user profile and other source data as required
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
// Populate the session with the details of the current user
session.PopulateWith<IAuthSession, MyUser>(CurrentUser);
// Save the session
authService.SaveSession(session);
return null;
}
public static void AddUserToRedis(MyUser user)
{
using(var redisClient = GetRedisClient())
{
// Get a typed Redis Client
var userClient = redisClient.As<MyUser>();
// Add the user to Redis
userClient.SetEntry(string.Format(UserKeyFormat, user.Username), user);
}
}
}
In the code above I have used a class MyUser to represent the user profile that I have stored in Redis, you can of course customise this class to match your user profile requirements. So this is the basic user profile class:
public class MyUser
{
public string Username { get; set; }
public string Password { get; set; } // Replace with a hashed password
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Setting up ServiceStack with Redis & your custom Authentication Provider:
You will need to configure ServiceStack to use Redis and tell it to use your custom authentication provider. You do this by adding the following to your Configure method in your AppHost:
public override void Configure(Funq.Container container)
{
// Configure ServiceStack to connect to Redis
// Replace with your connection details
container.Register<IRedisClientsManager>(c => new PooledRedisClientManager("127.0.0.1:6379"));
container.Register<ICacheClient>(c => c.Resolve<IRedisClientsManager>().GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);
// Add your custom credentials provider
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new MyRedisBasicAuthProvider()
}
));
// Add some test users. (If you have an existing Redis user source, you won't need to add test users.)
MyRedisBasicAuthProvider.AddUserToRedis(new MyUser {
Username = "john.smith",
Password = "test",
Email = "john.smith#email.com",
FirstName = "John",
LastName = "Smith",
});
MyRedisBasicAuthProvider.AddUserToRedis(new MyUser {
Username = "homer.simpson",
Password = "donuts",
Email = "homer.simpsons#springfield.com",
FirstName = "Homer",
LastName = "Simpson",
});
// Your other configuration settings ...
}
Notes:
In the example I haven't used a hash password, to keep the example straightforward, but this is trivial to do. Add another field public string Salt { get; set; } to the MyUser then instead of storing the plain password on MyUser store it as a hash of the password and salt i.e. hashedPassword = HashAlgorithm(password + salt). You already have code for it:
string hash, salt;
new SaltedHash().GetHashAndSaltString("password", out hash, out salt);
So this solution will now use a Redis data source to authenticate users when a service is secured using the [Authenticate] attribute. As with the standard basic provider, the credentials are authenticated at the standard /auth/basic route.
Using the Credentials provider instead of Basic:
If you want to use a credentials provider for form posts, instead of Basic authentication you can simple replace the word Basic with Credentials in the code above.
I hope this helps.

Intercepting an encrypted login token in a request

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");
}