I want Use this link:
https://docs.wso2.com/display/IS570/Adaptive+Authentication
I want Role1 Can't Login to my Application and Write this Code:
and it's Don't Work and Role1 can login to my application.
How can Write this script?
// Role-Based from Template...
// This script will step up authentication for any user belonging
// to one of the given roles
// If the user has any of the below roles, authentication will be stepped up
var rolesToStepUp = ['Role1'];
function onLoginRequest(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var user = context.currentKnownSubject;
// Checking if the user is assigned to one of the given roles
var hasRole = hasAnyOfTheRoles(user, rolesToStepUp);
if (hasRole) {
Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString());
return false;
}
}
});
}
// End of Role-Based.......
If you didn't get the log Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString()); check your condition. If your Role is an internal role, change rolesToStepUp variable as follows and try out.
var rolesToStepUp = ['Internal/Role1'];
Have a look on https://stackoverflow.com/a/66013019/10055162 for more details
In order to fail the user login, just returning false doesn't work. Use sendError utility function (https://docs.wso2.com/display/IS570/Adaptive+Authentication+JS+API+Reference#AdaptiveAuthenticationJSAPIReference-sendError(url,parameters)).
Check user-age-based script for sendError usage (https://docs.wso2.com/display/IS570/Configuring+User-Age-Based+Adaptive+Authentication)
Related
We are implementing MFA using Microsoft Azure. We run wso2 identity server 5.10.4.
What I would like to see happen, is when a user logs in, if they have certain memberOf AD roles, Federation happens. If not, it just uses basic auth/normal log in.
I've been approaching this problem using Adaptive Authentication custom scripting. On a service provider, I created two login steps. Step 1 is basicauth. Step 2 is our MS federated authenticator.
var arrayOfRoles = ["employee","student"];
var onLoginRequest = function(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var memberOfClaim = 'http://wso2.org/claims/employeeType';
var user = context.currentKnownSubject;
var roles = context.currentKnownSubject.localClaims[memberOfClaim];
foundRole = '-1';
var arrayOfRolesLen = arrayOfRoles.length;
for (var i = 0; i < arrayOfRolesLen; i++) {
searchRole = roles.indexOf(arrayOfRoles[i]);
if (searchRole >= 0) {
foundRole = searchRole;
}
}
if (foundRole >= 0 ) {
Log.info(user.username + ' found a role, indexof=' + foundRole);
// Step 2 is MFA
executeStep(2);
}
}
});
};
The script correctly finds the person's AD memberOf values, and I'm able to execute step 2 using this script. The problem is that a person logs in once to wso2, then if the roles are matched and exectuteStep(2) is invoked, they are prompted with another log in screen for wso2.
How can I prevent a second log in when a person matches the conditions for step 2? Or is this the wrong approach to making a role based decision about when to authenticate using basicauth and when to authenticate using federation?
edit:
Responding to some comments below about using identity-first.
If I setup three steps 1) id first, 2) basic 3) federated, I am prompted for the username again. Step one, a username prompt from wso2. Step two, a username and password prompt. Ditto with step 3.
One difference I see in the below screenshots, is that there is a 'username and password' authenticator. I don't have that available to me in the drop down boxes. Just jwt-basic, and basic. My script using three auth steps looks like this:
var arrayOfRoles = ["PCC_EMPLOYEE_ACTIVE","PCC_STUDENT_CREDIT"];
var onLoginRequest = function(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var memberOfClaim = 'http://wso2.org/claims/employeeType';
var user = context.currentKnownSubject;
var roles = context.currentKnownSubject.localClaims[memberOfClaim];
foundRole = '-1';
var arrayOfRolesLen = arrayOfRoles.length;
for (var i = 0; i < arrayOfRolesLen; i++) {
searchRole = roles.indexOf(arrayOfRoles[i]);
if (searchRole >= 0) {
foundRole = searchRole;
}
}
Log.info('found role is equal to: ' + foundRole);
if (foundRole >= 0 ) {
Log.info(user.username + ' found a role, indexof=' + foundRole);
// Step 3 is Azure idp.
executeStep(3);
} else {
// Step 2 is basic auth.
executeStep(2);
}
}
});
};```
You may try "identifier First" login with hasRole() to implement your flow.
i.e.
Step1 = Identifier
Step2 = Basic
Step3 = Azure
Let me explain what you have written here using the script.
So you have two auth steps.
Basic auth (username and password)
Federated IDP (Azure)
If you have not enabled adaptive script, after the basic auth step (step1), the user needs to authenticate against the FIDP (step2).
According to the current script that you have written, all the users will have a username and password authentication step (step1). Upon the success of that step, FIDP authentication will be enabled (step2) if the user has a certain set of roles.
Now consider a user who has those specified roles. When that user logins, first the user will see the username password login page. After that, the user will see the azure login page if the user has no active session in Azure (If SSO is enabled, the user might not see the second screen). So it is expected to see two login screens.
So what you have done here is correct, but the user experience is kind of weird. A similar scenario is explained here.
Edited: The better approach is to use the identifier first login method. You can read it here. Please find the approach that I have tried.
I have added 3 steps as follows.
Next I have added a role based adaptive script.
With this approach if I have the role admin, I will only see a input box to provide my password.
But for google it will show google sign-in. If you already have a session then only the consent will be seen. We cannot skip this since this is not in our control.
We use WSO2 5.10.1 for SSO and I am trying to get adaptive MFA working but having some trouble. The IP-based adaptive authentication template works, but for some reason, the role-based template does not. For some reason, the variable hasRole always returns false, even though my test user is a member of the internal role called staff2. staff2 is an internal role containing only the test user.
The wso2carbon.log with the info is below. I also included the script I am using, which is the default role-based template PLUS a few modifications I made to get a little more information in the log.
Log:
TID: [-1234] [] [2021-02-02 10:14:34,684] [badcbf3d-288c-4d3a-8abc-372763d87e0b] INFO {org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsLogger} - Working so far
TID: [-1234] [] [2021-02-02 10:14:34,684] [badcbf3d-288c-4d3a-8abc-372763d87e0b] INFO {org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsLogger} - false string info
TID: [-1234] [] [2021-02-02 10:14:34,684] [badcbf3d-288c-4d3a-8abc-372763d87e0b] INFO {org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsLogger} - <testuser> Has one of Roles: [staff2]
My Code:
// Role-Based from Template...
// This script will step up authentication for any user belonging
// to one of the given roles
// If the user has any of the below roles, authentication will be stepped up
var rolesToStepUp = ['staff2'];
var onLoginRequest = function(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var user = context.currentKnownSubject;
// Checking if the user is assigned to one of the given roles
var hasRole = hasAnyOfTheRoles(user, rolesToStepUp);
Log.info('Working so far');
Log.info(hasRole + ' string info');
if (5 > 1) {
Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString());
executeStep(2);
}
}
});
};
// End of Role-Based.......
Change your rolesToStepUp variable as follows and try out.
var rolesToStepUp = ['Internal/staff2'];
If you use the variable as var rolesToStepUp = ['staff2'];, it checks whether the user has a role named 'staff2' in the PRIMARY userstore. For example, if you create a Role by selection domain- PRIMARY and role name- staff2, the user who gets assigned to that role will be able to go through 2 steps according to your script.
If you want to use a role other than a role attached to PRIMARY userstore, you need to define the domain in the var rolesToStepUp array.
Since you have created an internal role, var rolesToStepUp should be defined as var rolesToStepUp = ['Internal/staff2'];
In our sharepoint farm we have two groups:
Users - Contains ALL users of our application (including administrators)
Admins - Contains users with admin privileges
I'm currently trying to deny access to System Pages (_layouts/ files) for all users in the "Users" group to prevent modifying lists etc. without going through the corresponding webpart UI.
To do this I added a permission policy with DENY on View Application Pages. This works as its supposed to for normal users, but the problem is it also blocks the administrators from accessing lists etc.
I tried granting everything to the Admins group, but the deny still overrides it preventing access.
How can I grant access to the admins when their user is blocked in another group?
You could use JSOM solution, check user groups and admin by JSOM(sample script from this thread).
Below script contains logic to check is user admin.
function IsCurrentUserMemberOfGroups(groups, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentWeb.get_currentUser();
var isAdmin = currentUser.get_isSiteAdmin();
var oGroups = currentUser.get_groups();
currentContext.load(isAdmin);
currentContext.load(currentUser);
currentContext.load(oGroups);
currentContext.executeQueryAsync(OnSuccess, OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
console.log('Admin check' + isAdmin);
if (!isAdmin) {
var groupEnumerator = oGroups.getEnumerator();
while (groupEnumerator.moveNext()) {
var oGroup = groupEnumerator.get_current();
var groupTitle = oGroup.get_title();
console.log(groupTitle);
$.each(groups, function (index, value) {
if (value == groupTitle) {
console.log('user in group ' + groupTitle);
userInGroup = true;
}
});
if (userInGroup)
break;
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
}
}
Then, redirect user to a friendly page.
Add the script to master page or add as javascript link as it need to be run globally.
SharePoint Custom JS file Best Practice
I want to use a phone number as the username for my app and i want to be able to make it simple to sign up by just having to verify the phone number each time they want to login - no messy password remembering business.
How to do this with AWS Cognito User Pool as its asking me to mandatorily configure a password for each user.
I thought of using a dummy password for each user and configure mandatory user verification. Everytime the user sign out i can "Unverify" the user so that next time they would automatically be asked to verify the phone number. Also i would wire up my app to only "login" if the user is verified.
Please let me know if the is the best approach :( I'm new to AWS and i could't find any posts for this scenario.
Thanks !!
Since AWS Cognito is not currently supporting passwordless authentication you need to implement a workaround with random password stored externally. You can implement the authentication flow as follows.
After user Signup (Also ask for mobile number and make it mandatory), store the Mobile number, Username and Password also in Dynamodb encrypted with AWS KMS (For additional security).
You can use MFA with mobile number for authentication challenge so that after the user enters mobile number and press login(In frontend), in the backend you can automatically do username password matching(Passthrough) and trigger the MFA to send a code for user's mobile and verify that using the AWS Cognito SDK (Without implementing custom mobile message and challenge).
If you plan to implement the flow manually(Without MFA) to send the SMS & validation, you can use AWS SNS for the purpose.
Check the following code sample to understand the insight of MFA and refer this link for more details.
var userData = {
Username : 'username',
Pool : userPool
};
cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
var authenticationData = {
Username : 'username',
Password : 'password',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
alert('authentication successful!')
},
onFailure: function(err) {
alert(err);
},
mfaRequired: function(codeDeliveryDetails) {
var verificationCode = prompt('Please input verification code' ,'');
cognitoUser.sendMFACode(verificationCode, this);
}
});
Note: Here the MFA with mobile number is not used for the purpose of MFA but as a workaround to meet your requirement.
This is a slightly different spin to what the OP is requesting as it uses a single secret, but I think it may help others who land on this question.
I was able to do this by creating custom lambdas for the Cognito triggers: Define Auth Challenge, Create Auth Challenge & Verify Auth Challenge.
My requirement was that I wanted my backend to use a secret to then get access & refresh tokens for any Cognito user.
Define Auth Challenge Lambda
exports.handler = async event => {
if (
event.request.session &&
event.request.session.length >= 3 &&
event.request.session.slice(-1)[0].challengeResult === false
) {
// The user provided a wrong answer 3 times; fail auth
event.response.issueTokens = false;
event.response.failAuthentication = true;
} else if (
event.request.session &&
event.request.session.length &&
event.request.session.slice(-1)[0].challengeResult === true
) {
// The user provided the right answer; succeed auth
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
// The user did not provide a correct answer yet; present challenge
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
}
return event;
};
Create Auth Challenge Lambda
exports.handler = async event => {
if (event.request.challengeName == 'CUSTOM_CHALLENGE') {
// The value set for publicChallengeParameters is arbitrary for our
// purposes, but something must be set
event.response.publicChallengeParameters = { foo: 'bar' };
}
return event;
};
Verify Auth Challenge Lambda
exports.handler = async event => {
if (event.request.challengeName == 'CUSTOM_CHALLENGE') {
// The value set for publicChallengeParameters is arbitrary for our
// purposes, but something must be set
event.response.publicChallengeParameters = { foo: 'bar' };
}
return event;
};
I was then able to use some JS, using amazon-cognito-identity-js, to provide the secret and get the tokens:
var authenticationData = {
Username : 'username'
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId : '...', // Your user pool id here
ClientId : '...' // Your client id here
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.setAuthenticationFlowType('CUSTOM_AUTH');
cognitoUser.initiateAuth(authenticationDetails, {
onSuccess: function(result) {
// User authentication was successful
},
onFailure: function(err) {
// User authentication was not successful
},
customChallenge: function(challengeParameters) {
// User authentication depends on challenge response
var challengeResponses = 'secret'
cognitoUser.sendCustomChallengeAnswer(challengeResponses, this);
}
});
this may work, but storing password in dynamoDB may have issues, considering security. instead we can try like this:
option#1: - user sign ups with username and password.
setup cognito triggers - we can use lambda functions.
A. Create Auth Challenge
B. Define Auth Challenge
C. Verify Auth Challenge Response
client app should implement CUSTOM_CHALLENGE authentication flow.
ask user to enter registered phone number, pass this in username field. trigger B will understand the request and passes flow to trigger A, Trigger A will generate random code 5. use AWS SNS service to send SMS to user mobile number
Trigger C will validate OTP and allows login
points to consider:
a. setup phone number as alias (select Also allow sign in with verified phone number)
b. make phone number field as verifiable (this allows user to receive OTP)
option#1: - user sign ups without username and password.
cognito setup
setup phone number as alias (select Also allow sign in with verified phone number)
make phone number field as verifiable (this allows user to receive OTP)
during signup don't ask user to provide username and password, just ask phone number
generate UUID for both username and password to be unique and pass these to cognito along with phone number
Cognito sends OTP code to user for account confirmation.
for phone number with OTP login setup triggers as explained in above option.
for triggers code,refer
aws cognito pool with multiple sign in options
I have built a website that uses AWS Cognito with the Userpool functionality.
If I leave the page, the login is forgotten, and after one hour the token expires.
I'd like the login to be remembered when the user closes their browser and comes back.
I'd also like the auth token to auto refresh instead of just giving errors after one hour.
How can I do this?
The user pool tokens are saved to local storage. And when calling getCurrentUser on a user pool object you can retrieve the last authenticated user object. After that, calling getSession should use the refresh token to retrieve new access tokens if they are expired such as in the example below. In the callback for getSession, you should have a valid session.
var poolData = {
UserPoolId : '...', // Your user pool id here
ClientId : '...' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
return;
}
console.log('session validity: ' + session.isValid());
//You should have a valid session here
});
}
For how to remember user, and not have to re-login after page reload, see answer here: https://stackoverflow.com/a/70383024/284651
For refreshing the token, see documentation & example code in the docs here: https://docs.amplify.aws/lib/auth/advanced/q/platform/js/#token-refresh
There is also some relevant discussion here:
https://github.com/amazon-archives/amazon-cognito-identity-js/issues/271