Login social with laravel socialite create password - laravel-7

How to inclues password after login with social network with laravel socialite? (sorry for bad english)
Com criar/incluir senha fazendo login a partir de uma rede social com o laravel socialite?
code on my LoginController
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->stateless()->user();
$authUser = $this->findOrCreateUser($user, $provider);
Auth::login($authUser, true);
return redirect($this->redirectTo);
//return $user->token;
//dd($userSocial);
}
public function findOrCreateUser($user, $provider)
{
$authUser = User::where('provider_id', $user->id)->first();
if ($authUser){
return $authUser;
}
return User::create([
'name' => $user->name,
'email' => $user->email,
'provider' => strToUpper($provider),
'provider_id' => $user->id
]);
}

In the socialite flow, a user will never need a password
You can set password to nullable() in the users migration file.
In 'Account Settings' a user can set a password, leaving the current password empty
Once the password is set, a socialite user can login via both social media or direct

I did two different implementations :
Generate a random password, save it with user object and send you
user an email with the auto generated password.
Create a middleware to check if password is NULL or not set and
redirect user to a form to enter password.

The objective of using the socialite authentification is that the user won't need a password.
BUT if somehow the user want to have a password, like i had earlier in one of my projects, he can easily click on forget password and he will receive a link via email to reset new password

Related

DDD modelisation issue (entity accessing repository)

I am designing the model of the following business needs :
The application must be able to register Users
The steps of the User registration are :
The user enters an email address and confirm
A verification code is sent to the provided email address.
The user must enter the correct verification code to continue
Repeat steps 1-3 for a phone number with verification code by SMS (optional)
The user then enters some personal information and confirm => the account is created
After registration, the user can update his email address or mobile phone number, but must go through the same verification process (code sent which must be entered to confirm the modification)
I ended up with the following model :
Verifiable (interface)
User (entity)
EmailAddress (value type, is a Verifiable)
MobilePhoneNumber (value type, is a Verifiable)
RandomCode (value type)
VerificationCode (entity containing a Verifiable, a RandomCode and a generationDateTime)
VerificationEmail (aggregate containing a VerificationCode, an EmailAddress and a Locale)
VerificationSms (aggregate containing a VerificationCode, a MobilePhoneNumber and a Locale)
Then here come the questions !!
Is it correct to have the Verifiable interface in order to have a VerificationCode instead of having EmailVerificationCode and SmsVerificationCode ? (Although it's not really a part of the ubiquitous language)
As I must persist somewhere the tuple emailAddress/mobilePhoneNumber + randomCode + generationDateTime to be able to retrieve it for verification, is it ok to have a specific entity for this ?
When the user wants to update his email address I was expecting to do something like :
// In the application service
User u = userRepository.findByUid(uid);
u.updateEmailAddress(newEmailAddress, enteredCode);
userRepository.save(u);
// In the User class
public void updateEmailAddress(EmailAddress newEmailAddress, String code) {
// Here comes the direct repository access
VerificationCode v = verificationCodeRepository.findByVerifiable(newEmailAddress);
if (v != null && v.hasNotExpired() && v.equalsToCode(code)) {
this.emailAddress = newEmailAddress;
verificationCodeRepository.delete(v);
}
else {
throw new IncorrectVerificationCodeException();
}
}
but to prevent my entity accessing a repository I ended up with the following code :
// In the application service
User u = userRepository.findByUid(uid);
VerificationCode v = verificationCodeRepository.findByVerifiable(newEmailAddress);
if (v != null && v.hasNotExpired() && v.equalsToCode(code)) {
verificationCodeRepository.delete(v);
u.updateEmailAddress(newEmailAddress);
userRepository.save(u);
}
else {
throw new IncorrectVerificationCodeException();
}
// In the User class
public void updateEmailAddress(EmailAddress newEmailAddress) {
this.emailAddress = newEmailAddress;
}
But it looks like an anemic model and the business logic is now in the application layer...
I am really struggling to correctly design the model as this is my first DDD project, any advice, modelisation suggestion is welcomed...
There is nothing wrong passing a repository as an argument in your updateEmailAddress() method.
But there is a better alternative, a domain service:
Your domain service depends on the repository and encapsulates the logic bound to your verification. You then pass this service to the user entity which is in charge of calling the correct method.
Here is how it could looks like:
class EmailVerificationService {
VerificationCodeRepository repository;
boolean isCodeVerified(EmailAddress emailAddress, String code) {
// do your things with the repository
// return true or false
}
}
Then in the user class:
class User {
// ...
public void updateEmailAddress(EmailVerificationService service, EmailAddress emailAddress, String code) {
if (service.isCodeVerified(emailAddress, code)) {
this.emailAddress = emailAddress;
} else {
// throw business Exception ?
}
}
}
In your application service, you inject the domain service and wire everything, catching the eventual exception and returning an error message to the user.
This is a suggestion of modeling, if you want to take it into account. Hope it could help you. I would model it this way:
User (aggregate root entity)
id
emailAddress (not null and unique)
mobilePhoneNumber (optional)
personalInfo
enabled (a user is created disabled when the registration process starts, and it is enabled when the process ends successfully)
VerificationCode (aggregate root entity) ===> it is associated to a user
id
randomCode
expirationDate
userId
smsOption (boolean) ===> if sms option is true, this verification code will be sent in a SMS to the user (otherwise it will be sent by email to the user)
Static Factory meethods:
forSendingByEmail ==> creates an instance with smsOption false
forSendingBySMS ===> creates and instance with smsOption true
Domain Service: sendVerificationCodeToUser ( verificationCodeId ) ===> checks smsOption to send either an SMS or an email (to the mobilePhoneNumber/emailAddress of the associated userId)
DomainEvent: VerificationCodeWasCreated ===> it has the id of the verification code that has been created
Raised by the VerificationCode constructor
The listener will call the domain service: sendVerificationCodeToUser(verificationCodeWasCreated.verificationCodeId())
THE REGISTRATION PROCESS (application service methods):
(1) The user enters an email address and confirm
public void registerUser ( String email ):
checks that doesn't exists any enabled user with the given email
if exist a disable user with the email, delete it
creates and persist a new disabled user with the email
creates and persist a new verification code associated to the created user for sending by email
(2) A verification code is sent to the provided email address ===> it is done by the domain event listener
(3) The user must enter the correct verification code to continue ===> the user who was sent the email in step (1) has to enter the email again, and the code he received)
public boolean isARandomCodeCorrectForUserEmail ( String randomCode, String email ) {
User user = userRepository.findByEmail(email);
if (user==null) {
return false;
}
VerificationCode vcode = verificationCodeRepository.findByRandomCodeAndUserId(randomCode,user.id());
if ( vcode==null) {
return false;
}
return vcode.hasNotExpired();
}
(4) Repeat steps 1-3 for a phone number with verification code by SMS (optional)
(4.1) The user of step (3) enters mobile phone number (we know the user id):
public void generateCodeForSendingBySmsToUser ( String mobilePhoneNumber, String userId ):
update user of userId with the given mobilePhoneNumber
creates and persist a new verification code associated to the user for sending by SMS
(4.2) The event listener sends the SMS
(4.3) The user who was sent the SMS in step (4.2) has to enter the email of step (1) again, and the code he received by SMS ===> isARandomCodeCorrectForUserEmail(randomCode,email)
(5) The user then enters some personal information and confirm ===> the account is created ===> what I do is enabling the user, since the user is already created, and we know the userId from step (3) or (4.3)
public void confirmRegistration ( PersonalInfo personalInfo, String userId ):
update user of userId with the given personalInfo
enables de the user
THE EMAIL/MOBILEPHONENUMBER MODIFICATION PROCESS:
It is similar to the registration, but the email/mobilePhoneNumber entered at the beginning must belongs to an existing enabled user, and at the end an update of the user is performed, instead of enabling.
ENABLED/DISABLED USERS:
Having enabled and disabled users, makes you taking it into account in authentication and authorization methods. If you don't want to or you're not allowed to have enabled/disabled users, you would have to model another aggregate that it would be UserCandidate or something like that, just with id, email and mobilePhoneNumber. And at the end of the process, create the real user with those values.

Azure B2C check user exist or not?

I am using Azure B2C, followed by the article
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
User is added successfully. But the issue is how to check the user exist or not with a user name, when I creating a new user?
You can find users by their email address or their user name using the signInNames filter.
For an email address:
`GET https://graph.windows.net/myorganization/users?$filter=signInNames/any(x:x/value eq 'someone#somewhere.com')&api-version=1.6`
For a user name:
`https://graph.windows.net/myorganization/users?$filter=signInNames/any(x:x/value eq 'someone')&api-version=1.6`
Programmatically, to check the user with the email address already exist.
here is a solution using C# and Graph client library.
private async Task<User> CheckUserAlreadyExistAsync(string email, CancellationToken ct)
{
var filter = $"identities/any(c:c/issuerAssignedId eq '{email}' and c/issuer eq '{email}')";
var request = _graphServiceClient.Users.Request()
.Filter(filter)
.Select(userSelectQuery)
.Expand(e => e.AppRoleAssignments);
var userCollectionPage = await request.GetAsync(ct).ConfigureAwait(false);
return userCollectionPage.FirstOrDefault();
}

OIM - PasswordMgmtService.validatePasswordAgainstPolicy : Issue with password history condition in policy being bypassed

I am working on a custom OIG password management requirement for a client.
I am facing issue while validating the password history in policy definition (eg: shouldn't match last 5 passwords used).
For some reason, PasswordMgmtService API's validatePasswordAgainstPolicy method is bypassing the history validation and returning true if user enters any old password.
Below is the code snippet for reference.
public ValidationResult validatePasswordRACFPolicy(String loggedinUserKey, char[] userPassword)
{
PasswordMgmtService pwdMgmtSvc = oimClient.getService(PasswordMgmtService.class);
User usr = new User(loggedinUserKey); //loggedinUserKey is user key of logged in user
ValidationResult valResult = pwdMgmtSvc.validatePasswordAgainstPolicy(userPassword, usr, <App Instance Name>, Locale.getDefault());
IDMLOGGER.log(ODLLevel.FINEST, "Is Password Valid = " + valResult.isPasswordValid()); //this value is true even if user tries to reset password using any older passwords.
return valResult;
}
Eventually, ending up with exception when I try to update the account password on target.
provSvc.changeAccountPassword(Long.valueOf(accountId), userPassword);
//provSvc is ProvisioningService API object, accountId is oiu_key, userPassword is the password entered by user.
Here are the exception details:
GenericProvisioningException An error occurred in oracle.iam.provisioning.handlers.ChangeAccountPasswordActionHandler/execute while changing the password for account with id 1234 and the casue of error is {2}.[[ at oracle.iam.provisioning.util.ProvisioningUtil.createEventFailedException(ProvisioningUtil.java:175) at oracle.iam.provisioning.handlers.ChangeAccountPasswordActionHandler.execute(ChangeAccountPasswordActionHandler.java:84 ... ... Class/Method: tcOrderItemInfo/validatePassword Error : Password Does Not Satisfy Policy

Remove default password value in drupal 6 password_confirm

I have this code for an email settings form that the user will input the email address, password, etc.
$form['mail_settings']['user_pass'] = array(
'#type' => 'password_confirm',
'#description' => t('your password')
);
$form['mail_settings']['user_signature'] = array(
'#type' => 'textfield'
'#description' => t('custm signature')
);
What I wanted to achieve is to have the user be able to change his signature anytime without having to re-enter his password all over again.
What's happening right now is that every time I load this settings page there's a default value for the password and blank for the password confirmation.
So, if the user forgets to input his password again, the form displays an error. Or rather the it will create a validation error.
What should be done here?
Came up with a different solution. It turned out that my browser's password chain (saved passwords) is responsible for adding the default value in the password field.
The solution was just, if password field is empty: update password using the old one; else if not empty update password with the new one.

Spring security integration with open id in grails

I am working on Integrating spring security with openId for my grails Application using springsecurity core and springsecurity openid plugins. I have integrated it, and it works well but I need to access the email for the logged in person. How can I get that, all that I am able to access is a token which is used for identifying the person.
Thanks to Ian Roberts.
He gives me this reply,Which exactly solves my problem.
His reply was:
As it happens I implemented exactly this in one of my applications
yesterday :-) Unfortunately it's not an open-source app so I can't just
point you at my code but I can explain what I did.
The spring-security-openid plugin supports the "attribute exchange"
mechanism of OpenID, although the support is not documented much (if at
all). How well it works depends on the provider at the far end but this
at least worked for me using Google and Yahoo.
In order to request the email address from the provider you need to add
the following to Config.groovy:
grails.plugins.springsecurity.openid.registration.requiredAttributes.email
= "http://axschema.org/contact/email"
Now to wire that into your user registration process you need an email
field in your S2 user domain class, and you need to edit the generated
OpenIdController.groovy in a few places.
add an email property to the OpenIdRegisterCommand
in the createAccount action there's a line
"if(!createNewAccount(...))" which passes the username, password and
openid as parameters. Change this along with the method definition to
pass the whole command object instead of just these two fields.
in createNewAccount pass the email value forward from the command
object to the User domain object constructor.
And finally add an input field for email to your
grails-app/views/openId/createAccount.gsp.
You can do the same with other attributes such as full name.
grails.plugins.springsecurity.openid.registration.requiredAttributes.fullname
= "http://axschema.org/namePerson"
The important thing to wire it together is that the thing after the last
dot following requiredAttributes (fullname in this example) must match
the name of the property on the OpenIdRegisterCommand.
Regards
Charu Jain
I've never used the springsecurity openid plugin, but when using springsecurity core you can expose additional information about the current user by implmenting a custom UserDetails. In my app, I added this implementation, so that I can show the name property of logged-in users. You'll need to change this slightly, so that the email address is exposed instead
/**
* Custom implementation of UserDetails that exposes the user's name
* http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/11%20Custom%20UserDetailsService.html
*/
class CustomUserDetails extends GrailsUser {
// additional property
final String name
CustomUserDetails(String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id,
String displayName) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id)
this.name = displayName
}
}
You then need to create a custom implementation of UserDetailsService which returns instances of the class above
class UserDetailsService implements GrailsUserDetailsService {
/**
* Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
* we give a user with no granted roles this one which gets past that restriction but
* doesn't grant anything.
*/
static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]
UserDetails loadUserByUsername(String username, boolean loadRoles) {
return loadUserByUsername(username)
}
UserDetails loadUserByUsername(String username) {
User.withTransaction { status ->
User user = User.findByUsername(username)
if (!user) {
throw new UsernameNotFoundException('User not found', username)
}
def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}
return new CustomUserDetails(
user.username,
user.password,
user.enabled,
!user.accountExpired,
!user.passwordExpired,
!user.accountLocked,
authorities ?: NO_ROLES,
user.id,
user.name)
}
}
}
You need to register an instance of this class as a Spring bean named userDetailsService. I did this by adding the following to Resources.groovy
userDetailsService(UserDetailsService)

Resources