In my app, there are two types of users. A group of them, as admins, must register themselves, and the other can only be present after being invited by the admins. Both types of users get verification email. I want the page that these two types of users see, after click on button in verification email, is different. How can I do this?
Thanks for any help
If You are using laravel/ui for authentication you need to provide path where the user is to be taken based on type in your VerificationController which ensure where to redirect your user after it is verified.
you need to remove protected $redirectTo = RouteServiceProvider::HOME; and replace with below function
public function redirectTo()
{
if(auth()->user()->type == 'admin'){
return '/here';
}else{
retuurn 'there'
}
}
if you are using anything other than laravel/ui you can follow the same procedure and have to redirect manually to certain url
Related
I am hoping someone can point me in the right direction. We host a University Moodle site and we are looking for a way in which we can perform extra validation on a Student whenever they login. I will give a scenario.
We have an endpoint with a list of email addresses of students allowed to use the system, for example a list of Students who are fully paid up on tuition. Therefore, we are looking for a way to hook into the login process, perform this check and the allow the student to continue or redirect back to the login page with an error.
I would appreciate any advice on how we can achieve this. Thank you.
I found a solution to my problem. I ended up creating a custom Authentication plugin using the guidelines from https://docs.moodle.org/dev/Authentication_plugins. With that knowledge, I used the copied the folder in the Moodle installation path auth/none and used that as a shell for my new plugin. I went ahead and customized the plugin names to what I needed. Once that was done and once the plugin was installed and enabled from the Administrator Dashboard, I had something like this in my auth.php file:
// Required for all auth plugins
public function user_login($username, $password)
{
return false;
}
// Hooks in immediately after the User submits the login form
public function loginpage_hook()
{
$username = $_REQUEST['username'] ?? '';
/** CODE CHECKING IF USERNAME IS ALLOWED TO ACCESS MOODLE **/
/** FOR EXAMPLE CHECK IF USER PAID FEES **/
$userHasPaidFees = api_checks_if_user_paid_fees($username);
if ($userHasPaidFees ) {
// Returning true here proceeds with the
// normal Username/Password login combination
return true;
}
// If not, redirect them back to Login
// Or any other page and notify
redirect(
new moodle_url('/login/index.php'),
'Message telling user why they were not able to sign in',
null,
\core\output\notification::NOTIFY_ERROR
);
}
Thanks and I hope someone finds this useful.
I would like to implement this authentication flow in Keycloak:
A user creates an account by typing only his email
The user is logged in and can access my service
2'. At the same time, an email is sent to him, allowing him to "finalize" his account
The user leaves his session -> to reuse my service, he must click in the received email
By clicking in the received email, the user defines his first password
The user is then logged in automatically (without going through a login page).
The objective of this flow is to be the simplest, to hook users who are not used to webapps.
The implementation I would do:
Create an account without password request: I customize the Keycloak Registration flow by disabling the Password Validation and Profile Validation rules
Programmatically, in my webapp, at the first connection of a user, via the REST Admin API, I trigger the email action UPDATE_PASSWORD
I get something that works, but:
A. The link received by email redirects to an intermediary page confirming the execution of actions ("Perform the following action (s)") - (similar to Keycloak Implement Reset password flow same as forgot password flow)
B. The user is then redirected to a login page, and not directly connected to the application.
When, as a normal user, I trigger a reset password request (through 'forget password' feature), the process is the one I want: by clicking on the email link, I go directly to the page allowing me to enter and confirm a new password, then I'm authenticated.
My question: Do you see a way to implement this 'simplified' flow?
My keycloak version : 11.0.2
Thank you !
I could remove the "info.ftl" page display, customizing the "ExecuteActionsActionTokenHandler", as explained here :
action-token-spi
You have to create a file :
src/main/resources/META-INF/services/org.keycloak.authentication.actiontoken.ActionTokenHandlerFactory
containing the name of the class you want to use instead :
com.example.ExecuteActionTokenHandlerFactory
Then you create that class com.example.ExecuteActionTokenHandlerFactory with the following code :
public class ExecuteActionTokenHandlerFactory extends ExecuteActionsActionTokenHandler {
#Override
public Response handleToken(ExecuteActionsActionToken token, ActionTokenContext<ExecuteActionsActionToken> tokenContext) {
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
String redirectUri = RedirectUtils.verifyRedirectUri(tokenContext.getUriInfo(), token.getRedirectUri(),
tokenContext.getRealm(), authSession.getClient());
if (redirectUri != null) {
authSession.setAuthNote(AuthenticationManager.SET_REDIRECT_URI_AFTER_REQUIRED_ACTIONS, "true");
authSession.setRedirectUri(redirectUri);
authSession.setClientNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUri);
}
token.getRequiredActions().stream().forEach(authSession::addRequiredAction);
UserModel user = tokenContext.getAuthenticationSession().getAuthenticatedUser();
// verify user email as we know it is valid as this entry point would never have gotten here.
user.setEmailVerified(true);
String nextAction = AuthenticationManager.nextRequiredAction(tokenContext.getSession(), authSession, tokenContext.getClientConnection(), tokenContext.getRequest(), tokenContext.getUriInfo(), tokenContext.getEvent());
return AuthenticationManager.redirectToRequiredActions(tokenContext.getSession(), tokenContext.getRealm(), authSession, tokenContext.getUriInfo(), nextAction);
}
}
Actually it is the same implementation as the upper class, except we removed the following part :
if (tokenContext.isAuthenticationSessionFresh()) {
...
}
which means that if the user did not have a session, which happens when the user is reseting his password, he is redirected to that "info.ftl" page.
As a workaround for problem A, I customize info.ftl template page. I add an ugly inline script to click on the link, redirecting automatically to the update password page.
<#import "template.ftl" as layout>
(...)
<#elseif actionUri?has_content>
<p><a id="yolo" href="${actionUri}">${kcSanitize(msg("proceedWithAction"))?no_esc}</a></p>
<script>document.getElementById('yolo').click()</script>
(...)
It'll do the job until I found a cleaner solution.
At the moment, B problem remains.
I'm using Sails with Passport for authentication. I'm using passport-google-oauth(OAuth2Strategy) and passport-facebook for enabling Google Sign-in.
I'm not too well-versed with Passport, so pardon me if this is a rookie question. I've set up login via Facebook and it works just fine. With Google, I do receive an authorization code after allowing access to the app, but the I'm eventually not authenticated. I'm guessing the same code should work for both Facebook and Google since the strategies are both based on oauth2.
I'm not even sure what code to share, since I'm using the auto-generated code from sails-generate-auth, but do let me know if there's anything else I can share.
Any ideas on why this might be happening? The app is locally hosted but that's unlikely to be the problem since I am getting to the authorization stage anyway.
I faced the same problem and it was located here in in api/services/passport.js:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
The Google service was not returning a profile.username property.
Because of it, the user is not saved in the database and cannot be authenticated. Then the passport callback receives an empty user, so the function that handles errors is fired and the user is redirected to the login page.
This change allows to use the displayName property as the username:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
/** Content not generated BEGIN */
// If the username property was empty and the profile object
// contains a property "displayName", add it to the user.
if (!user.username && profile.hasOwnProperty('displayName')) {
console.log(profile); // <= Use it to check the content given by Google about the user
user.username = profile.displayName;
}
/** Content not generated END */
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
You could also use the profile.id property because profile.displayName is not necessarily unique (ie: two Google accounts can have an identical displayName). But it is also true accross different services: a Twitter account could also have the same username than a Facebook account. If both register on your application, you will have a bug. This is a problem from the code generated by sails-generate-auth and you should adapt it with the behavior that you want.
I will propose a PR if this solution works for you too.
Alright, so this ultimately turned out to be a known issue with the API.
TL;DR: Enable the Google+ API and the Contacts API as mentioned here. (The Contacts API isn't required, as #AlexisN-o pointed out in the comments. My setup worked as desired with Contacts API disabled. This obviously depends on what scope you're using.)
I believe it's not a nice way of failing since this was an API error that was prevented from bubbling up. Anyway, I dug into passport.authenticate to figure out what was going wrong. This eventually calls the authenticate method defined in the package corresponding to the strategy (oauth2 in this case). In here (passport-google-oauth/lib/passport-google-oauth/oauth2.js) I found that the accessToken was indeed being fetched from Google, so things should be working. This indicated that there was a problem with the requests being made to the token urls. So I ventured a little further into passport-oauth2/lib/strategy.js and finally managed to log this error:
{ [InternalOAuthError: failed to fetch user profile]
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 403,
data: '{
"error": {
"errors": [{
"domain": "usageLimits",
"reason": "accessNotConfigured",
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration.",
"extendedHelp": "https://console.developers.google.com"
}],
"code": 403,
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration."
}
}'
} }
This was the end of the hunt for me and the first result for the error search led to the correct answer. Weird fix though.
Apparently when calling FB.login, the default permissions the user will have to give is this:
"xxx will receive the following info: your public profile and friend list".
And I know that I can add extra permissions with: { scope : 'email' } and so on.
The problem is, I don't want the whole public profile and the friend list.
The only thing I want is: first name, last name and email.
Is it possible to exclude the basic permissions and just have the ones I want?
No, not possible.
https://developers.facebook.com/docs/facebook-login/permissions/:
“When someone connects with an app using Facebook Login, the app can access their public profile and friend list, the pieces of information that are visible to everyone. ”
I'm using the Facebook requestdialog to show my friend list and invite,this in a Facebook Web APP, using this:
function sendRequestViaMultiFriendSelector() {
FB.ui(
{
method: 'apprequests',
message: 'Selección de amigos a los que invitar a KugaBar'
}, requestCallback);
}
function requestCallback(data) {
console.log(data);
}
It works (ok).
But I need to identify the user when enter in my APP, you see the requestCallback data and see a "request", but when the users click on the notification and enter in the APP
I don't know how identify the request to detect if the user is accessing by the notification.
EDIT: OK I found it.
The param in the GET vars are "request_ids", but one problem appear, this param only come if the user previously have accepted the permissions, if the user clicks on the notification, and accept the permissions, hitting enter: this param doesn't exist, if the user enter again (not need to accept nothing) this param exists in the URL.
Are there any method to get this param in the first time the user enter?
Thanks
When the user first clicks on a request, you should get the request_id from the URL and store it in a cookie. When the user has completed logging in, check if the cookie exists and then update your record accordingly.
Notifications and Requests are NOT the same thing on Facebook. In your case you're talking about Requests. Request ids are passed to the app as a GET parameter 'request_ids' which is a comma separated list.
To be able to access this parameter after login dialog, you want to save your GET parameters from the original launch URL before redirecting to the OAuth dialog and pass them around in the redirect URL. This way you solved this problem for all of your future GET parameters.
If you're using PHP, the function you're looking for is http_build_query. I'd highly discourage from using a cookie to store one-time and easily accessible URL parameters.