Exception in Site.createExternalUser in Apex RESTclass: Site.ExternalUserCreateException: [That operation is only allowed from within an active site.] - salesforce

I have a Non-Salesforce Auth System which holds usernames and passwords for a few thousand users. I am willing to migrate these users to Salesforce and give access to these users to my Experience Cloud site. I am developing an apex REST Resource which will take username and password as arguments and create a user with that username and password with a community profile. I am planning to call this API from my Non-Salesforce system and migrate all these users. I am using Site.createExternalUser method in this API. I am getting the exception
Site.ExternalUserCreateException: [That operation is only allowed from within an active site.]
The reason I am using Site.createExternalUser is because I don't want to send the welcome email/reset password email to my users since they already have signed up successfully long ago.
I am open to any alternatives for achiving this.
Below is my code:
#RestResource(urlMapping='/createUser/*')
global with sharing class createUserRestResource {
#HttpPost
global static String doPost(){
Contact con=new Contact();
con.Firstname="First";
con.LastName= "Last";
con.Email="first.last#example.com";
con.AccountId='/Add an account Id here./';
insert con;
usr.Username= "usernameFromRequest#example.com";
usr.Alias= "alias123";
usr.Email= "first.last#example.com";
usr.FirstName= "First";
usr.IsActive= true;
usr.LastName= "Last";
usr.ProfileId='/Community User Profile Id/';
usr.EmailEncodingKey= 'ISO-8859-1';
usr.TimeZoneSidKey= 'America/Los_Angeles';
usr.LocaleSidKey= 'en_US';
usr.LanguageLocaleKey= 'en_US';
usr.ContactId = con.Id;
String userId = Site.createExternalUser(usr, con.AccountId, 'Password#1234', false);
return userId;
}
}

You can suppress sending emails out in whole org (Setup -> Deliverability) or in the Community config there will be way to not send welcome emails (your community -> Workspaces -> Administration -> Emails).
Without running on actual Site I don't think you can pull it off in one go. In theory it's simple, insert contact, then insert user. In practice depends which fields you set on the user. If it's Partner community you might be setting UserRoleId too and that's forbidden. See MIXED DML error. In Customer community you might be safe... until you decide to assign them some permission sets too.
You might need 2 separate endpoints, 1 to create contact, 1 to make user out of it. Or save the contact and then offload user creation to #future/Queueable/something else like that.

Related

How to add grant type into access token in IdentityServer4?

I need to know the client's grant type (or OAuth Flow type) in my API protected by IdentitySrever4, but not sure how to do that. I am assuming that I need to add the grant type to the access token. Can someone help me by pointing me to instructions/documentations or sample code?
UPDATE
Under standard IdentityServer4 EF model, my SQL Server data store has a ClientGrantTypes table and a ClientClaims table (see screenshot below). I am assuming that I need to create a ClientClaims record that ties into ClientGrantTypes. If it is SQL, you can just join the tables on ClientID, but how do you implement it here to get the Grant Type into access token?
It would be easier to find the answer if you explained the purpose for the requirement. From my experience one common task is to distinguish authorization_code and client_credentials flow use for the same client, but that's easy: the second one does not contain user information (sub and sid claims).
Also don't forget about restricted auth flow combinations in Identityserver (for instance you can't allow both implicit and authorization_code flow for the same client), so one client is usually bound to the only user interactive flow.
Finally, the auth flow is generally not about API. It's only about interaction among IdP and Client. API usually use scopes as general information about clients, so... when you have two clients -- one with implicit grant and the other with authorization_code, you can distinguish which one is in use by setting different scopes.
Isn't that enough?
A check for a particular grant type could be performed in Identityserver the following way:
public class ExtendedClaimsService : DefaultClaimsService{
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(
ClaimsPrincipal subject,
ResourceValidationResult resourceResult,
ValidatedRequest request)
{
var outputClaims = (await base.GetAccessTokenClaimsAsync(subject, resourceResult, request)).ToList();
//if (request.Secret.Type == "NoSecret") //this is more or less the same
if ((request as ValidatedTokenRequest)?.GrantType != "client_credentials")
{
//filter out server-side-only scopes here
//or add any custom claim you like
}
return outputClaims;
}
}
Registration: services.AddTransient<IClaimsService, ExtendedClaimsService>(); after services.AddIdentityServer() in your Startup
One option is to have different client definitions for the different flows and then use a ClientClaim to indicate what type of client it is:
To set the client claims in the client definition just set it like:
ClientClaimsPrefix="",
AlwaysSendClientClaims=true,
Claims = new List<ClientClaim>()
{
new ClientClaim("role","admin"),
new ClientClaim("name","joe"),
new ClientClaim("admin","yes"),
new ClientClaim("employmentid","employee"),
new ClientClaim("employeetype","yes"),
new ClientClaim("creditlimit","100000")
}

Laravel 8 Fortify User UUID Login Problem

I am currently setting up a new project using Laravel 8. Out of the box, Laravel is configured to use auto-incrementing ID's for the user's ID. In the past I have overrode this by doing the following.
Updating the ID column in the user table creation migration to
$table->uuid('id');
$table->primary('id');
Adding the following trait
trait UsesUUID
{
protected static function bootUsesUUID()
{
static::creating(function ($model) {
$model->{$model->getKeyName()} = (string) Str::orderedUuid();
});
}
}
Adding the following to the user model file
use UsesUUID;
public $incrementing = false;
protected $primaryKey = 'id';
protected $keyType = 'uuid';
On this new project, I did the same as above. This seems to break the login functionality. When the email and password are entered and submitted, the form clears as though the page has been refreshed. Thing to note is there are no typical validation error messages returned as would be expected if the email and/or password is wrong.
To check that the right account is actually being found and the password is being checked properly, I added the following code to the FortifyServiceProvider boot method. The log file confirms that the user is found and the user object dump is correct too.
Fortify::authenticateUsing(function(Request $request) {
\Log::debug('running login flow...');
$user = User::where('email', $request->email)->first();
if ($user && Hash::check($request->password, $user->password)) {
\Log::debug('user found');
\Log::debug($user);
return $user;
}
\Log::debug('user not found');
return false;
});
Undoing the above changes to the user model fixes the login problem. However, it introduces a new problem that is the login will be successful but it wont be the right account that is logged in. For example, there are 3 accounts, I enter the credentials for the second or third account, but no matter what, the system will always login using the first account.
Anyone have any suggestions or ideas as to what I may be doing wrong, or if anyone has come across the same/similar issue and how you went about resolving it?
Thanks.
After digging around some more, I have found the solution.
Laravel 8 now stores sessions inside the sessions table in the database. The sessions table has got a user_id column that is a foreign key to the id column in the users table.
Looking at the migration file for the sessions table, I found that I had forgot to change the following the problem.
From
$table->foreignId('user_id')->nullable()->index();
To
$table->foreignUuid('user_id')->nullable()->index();
This is because Laravel 8 by default uses auto incrementing ID for user ID. Since I had modified the ID column to the users table to UUID, I had forgotten to update the reference in the sessions table too.

Salesforce- Creating contact and user by importing CSV file & send notification email to User

I want to create the contact & user related to account by importing csv file.After creating user, generated username & password should be send to the respective user's email address.
I am planning to use Vf page for accepting the csv file & the Contact & User API for adding contacts & Users. But I am not sure regarding the email notification to user through API.
So can anyone please provide me the best solution for this?
You'll need to use Database.insert() flavor instead of straightforward insert users;
Check out the DML options help topic, especially the "emailHeader" property
Something like this should do the trick:
List<User> users = new List<User>(); // fill in with data from your CSV
Database.DMLOptions dlo = new Database.DMLOptions();
dlo.EmailHeader.triggerUserEmail = true;
database.insert(users, dlo);
Be aware though that you can't insert an inactive user. You can deactivate them straight after the insert but then they can't login so you'll probably want to suppress the sending of email, not enforce it. Make sure you have sufficient amount of licenses before you start!
Last but not least - you can cheat by setting the same password for them with System.setPassword('user id', 'new password') :) It's not recommended to expose this functionality to your end users though, can process only 1 user at a time and wastes 1 DML statement...

cakephp authenticate user with repeated entries in the Database table (manual authentication?)

I'm creating an authentication system for a group of websites. The problem is that I have to use a pre-existing Database, which has a users table already full of entries, and that one user can have several accounts. Basically, a user has one account per website he has access to (it's not the best way to do this, but I can't change it). Each account is represented by an entry in the users table, with login, password, name... and the important field: website_id. This field tells the system what website that account has access to.
The big problem is that some users with more than one account have the exact same login/password information for all of them. For example, one user has 3 accounts:
account1: login = charly / pwd = 1234 / name = Charles ... website_id = 1
account2: login = charly / pwd = 1234 / name = Charles ... website_id = 2
account3: login = charly / pwd = 1234 / name = Charles ... website_id = 3
So if he goes to the website that has id = 2 and uses those credentials, he's granted access. If he goes to the website that has id = 4, he's denied access.
My problem is that since CakePHP does the login automatically, when a user tries to login, CakePHP checks only the first entry in the Database that matches the login/password submited in the form. So if a user is currently in the website with website_id = 3 and tries to login, Cake finds the first entry (account1), compares its website_id (1 in this case) to the current website's id (3), and since they're different, the access is not granted, but it should. _Please note that the comparison of the website_id vs the account's website_id is already being made manually in the login() function_.
This how the login() function looks like now:
function login() {
$userInfo = $this->Auth->user();
if ( isset($userInfo) ) {
if ($userInfo['User']['website_id'] == $this->website_id) {
//Users gets access to a website that he has an account for
}
else {
//User is denied access because his account is not registered for the current website
$this->Session->destroy();
$this->Session->setFlash(__('You don't have access to this website', true));
$this->redirect($this->Auth->logout());
}
}
}
What I would like is to be able to manually authorize the access to the current website by using the login/password submitted by the user to manually search in the users table, and if I find a match in one of the user accounts, grant the access, or otherwise deny access. To sum up, avoid all the automagic of Auth's component.
If the Auth component's login method fails, control is transferred back to the custom login action (e.g. UsersController::login()). I've used this to authenticate using either username or email address, but it could be easily adapted for this purpose. Same idea, different criteria. I offered what I think is a reasonably thorough response (with code) to a similar question. It may help you as well.

app on GAE | Restricted Google account authentications | listed google accounts not all |

I am quite new to this Google app engine. I am learning things every day.
I am have a forum on google app engine.
But I want is to be having private or restricted parts.
Some features should be locked for certain google account users, which are in some sort of access control list.
I plain words I can say, only those user who are in list of access can see the forum rest will be redirect to the "contact to admin" page.
As I am new I wanna know that is it something possible.
if yes, how can I achieve it ?
Thanks,
Alok
If you are using the built-in Users API, you can check users.is_current_user_admin() as an access control mechanism. Administrators can be managed via the dashboard.
If you need more granular, application-specific authorization logic, generally you would create a User model in the datastore that references the built-in UserProperty and also holds a list of roles or whatever else you need to check authorization.
To follow up Drew's reply, I use a similar system in my app, so my server code has something like the following class definition (simplified here for clarity)
class myUser(db.Model):
user = db.UserProperty(required=True)
rights = db.StringProperty(required=True, choices=set(["public", "private"]))
created = db.DateTimeProperty(auto_now_add=True)
lastaccess = db.DateTimeProperty(auto_now=True)
and then I have code like this where I handle queries
def checkUserRights(user):
q = db.GqlQuery("SELECT * from myUser WHERE user = :1", user)
u = q.get()
if not u:
# create a new 'public access' user if we haven't seen this person before
u = myUser(user=user, rights="public")
# always update the user record after the source is fetched (updates the lastaccess field)
db.put( u )
return u.rights
rights = checkUser(users.get_current_user())
if isPrivateArea and rights == "private":
....
This way I create a user for EVERY visitor, and then I have an admin interface to change the rights of selected users - you may decide, for example, to not create a record for every visitor
def checkUserRights(user):
q = db.GqlQuery("SELECT * from myUser WHERE user = :1", user)
u = q.get()
if not u:
# grant default public rights to anyone...
return "public"
# always update the user record after the source is fetched (updates the lastaccess field)
db.put( u )
return u.rights
This is, of course, on a page where the app.yaml specifies "login: required"

Resources