I have a function that allows the end user to execute a Workflow (containing many APIs) or schedule it to run as a background job.
Example: User1 creates Workflow1, which contains 3 APIs (Api1, Api2, Api3), and configures it to run at 9AM every day.
I use HttpClient to call each API like this:
var client = new HttpClient { BaseAddress = new Uri("http://localhost/") };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PostAsJsonAsync("/api/services/myApp/workflow/Api1?input=something", "").Result;
How do I add the credentials of User1 to the request while the user is not logged in to the application (because it will run automatically as a scheduled job)?
Update 1
I decided to use reflection to call an API by string name.
In the case of executing an API directly, how do I run it under a specific permission?
Update 2
I have put my code inside a using block, but all APIs were fired successfully:
using (_session.Use(1, 3)) // 3 is the Id of User1, who has no permissions
{
// Execute operator
switch (input.Operator.Type)
{
case "api":
executeApiResult = await ExecuteApi(input);
break;
case "procedure":
executeApiResult = await ExecuteProcedure(input);
break;
default:
return new ExecuteOperatorOutput
{
Result = new ExecuteOperatorResult { Status = false, Message = $"Wrong operator type: {input.Operator.Type}" },
WorkflowStatus = false
};
}
}
In the case of executing an API directly, how do I run it under a specific permission?
You can override current session values and call your method inside the using block.
I have put my code inside a using block, but all APIs were fired successfully
Declare your API methods as public virtual as there are some restrictions for AbpAuthorize.
You have two options.
1- You can make those Application Services anonymously accessible. And if you want it to be secure, send an encrypted security token.
2- You didn't mention if your project is MVC or Angular. I assume you have Angular version. You need a bearer token to make authenticated requests. First you have to authenticate user and get a token. Then add this bearer token to every request.
You have to research for using bearer tokens in asp.net core...
Related
Is it possible to call a cloud function that returns objects without having a current user? The iOS and Android SDKs support anonymous users but I'm asking specifically for JavaScript.
I'd like to allow it so that anyone who visits my web app can read objects without having to sign in. I'm using Back4App.
Yes. You can call a cloud code function no matter the user is logged in or not. Inside the cloud function you can check the user property of the request object to check if the user is either logged in or not. In the case that your user is not logged in and you want to query a class which requires user permission, you can use the useMasterKey option.
Parse.Cloud.define('myFunction', async req => {
const { user, isMaster } = req;
if (isMater) {
// the cloud code function was called using master key
} else if (user) {
// the cloud code function was called by an authenticated user
} else {
// the cloud code function was called without passing master key nor session token - not authenticated user
}
const obj = new Parse.Object('MyClass');
await obj.save(null, { useMasterKey: true }); // No matter if the user is authenticated or not, it bypasses all required permissions - you need to know what you are doing since anyone can fire this function
const query = new Parse.Query('MyClass');
return query.find({ useMasterKey: true }) // No matter if the user is authenticated or not, it bypasses all required permissions - you need to know what you are doing since anyone can fire this function
});
I am trying to integrate several Google API calls into a custom Drupal 8 module.
I am basically trying to first get my custom class to get an access token from Google via OAuth before I try do anything else. I am doing this by using a class function with everything simply in one place. The function is as follows:
public function testTokenRequest(): void
{
// Setup Google Client Config within context of initialized class
$this->googleClient->setClientId($this->googleClientID);
$this->googleClient->setClientSecret($this->googleClientSecret);
$this->googleClient->setDeveloperKey($this->googleApiKey);
// Add Google MyBusiness scope
$this->googleClient->setScopes(array('https://www.googleapis.com/auth/plus.business.manage'));
try {
$accessToken = $this->googleClient->getAccessToken(); // null returned where breakpoint hit
$this->googleAccessToken = $accessToken; // Put xdebug breakpoint here
} catch (Exception $exception) {
echo $exception->getMessage();
}
}
Currently all I get is a null returned for the $accessToken = $this->googleClient->getAccessToken(); call.
Unsure where I am going wrong, possibly the AddScopes call because the vendor documentation for the apiclient does this slightly differently, i.e. $client->addScope(Google_Service_Plus::PLUS_ME); but I couldn't find the correct class to use for the MyBusinessAPI scope so used the OAuth playground string instead https://www.googleapis.com/auth/plus.business.manage
I get an OAuth Playground AccessToken returned when I use that but end up with a Permission Denied error instead even though I have the GMB API added to my whitelist under credentials.
Google MyBusiness is Oauth2 based. The access token is not received until the user has approved your app, it is normal that the function returns null if the user has not approved the app yet.
Here is an example on how you create a link where to send the user to start authentication and authorization for your app.
$client = new \Google_Client();
$client->setAuthConfig(getcwd() . '/../client_secret.apps.googleusercontent.com.json');
$client->setAccessType("offline"); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
$client->addScope(
array(
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.business.manage'
)
);
$client->setRedirectUri('http://server.com/code');
$client->setApprovalPrompt('force');
return new Response(
'<html><body>Authenticate here : <a href="' .
$auth_url = filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL)
. '">HERE</a></body></html>'
);
The example above assumes your server will also implement a /code endpoint where the user is redirected to with the authorization token, then you need to call the api to exchange the token with the access and refresh code.
This document will help you understand further
https://developers.google.com/api-client-library/php/auth/web-app
I am trying to migrate users to Cognito when they sign in the first time. For this I wrote a lambda function that does call an API to check if the users exist in db or not ? if the user exists, it will be created in cognito but I am not sure how do I tell the application that user is created and it should allow the user to login .
Here is the code in c#:
public async Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)
{
RootObject rootObj = DeserializeStream(stream);
User user = new User(rootObj.userName, rootObj.request.password);
ApiResponse apiResponse = await MobileAuthenticateAsync(user.UserName, user.Password);
// Considering apiResponse returns "user authenticated", we create the user in //cognito. This is working.
// How do I send response back to Application so it knows that user is // //created and authenticated and should be allowed to login.
//Before returning stream, I am setting following 2 status.
rootObj.response.finalUserStatus = "CONFIRMED"; // is this correct ?
rootObj.response.messageAction = "SUPPRESS";
return SerializeToStream(rootObj);;
}
You're pretty close.
You can see the full documentation on the Migrate User Lambda Trigger page, however in short you need your response to look like:
{
response: {
userAttributes: {
email: 'user#example.com',
email_verified: true,
custom:myAttribute: 123,
},
finalUserStatus: 'CONFIRMED',
messageAction: 'SUPPRESS',
forceAliasCreation: false,
}
}
Where:
userAttribute: this is a dictionary/map of the user's attributes keys in cognito (note that any custom attributes need to be prefixed with custom:), to the values from the system you're migrating from. You do not need to provide all of these, although if you're using an email alias you may want to set email_verified: true to prevent the user having to re-verify their e-mail address.
finalUserStatus: if you set this to CONFIRMED then the user will not have to re-confirm their email address/phone number, which is probably a sensible default. If you are concerned that the password is given as plain-text to cognito this first-time, you can instead use RESET_REQUIRED to force them to change their password on first sign-in.
messageAction: should probably be SUPPRESS unless you want to send them a welcome email on migration.
forceAliasCreation: is important only if you're using email aliases, as it stops users who manage to sign-up into cognito being replaced on migration.
If you respond with this (keeping the rest of the original rootObj is convenient but not required then the user will migrated with attributes as specified.
If you throw (or fail to respond with the correct event shape) then the migration lambda fails and the user is told that they couldn't migrated. For example, because they do not exist in your old user database, or they haven't provided the right credentials.
I am using Owin middleware to implement token-based security for my application. When issuing the access token to the client I would also like to pass User Id along with the token, so that, the client application will know the User Id and will be able to call GetUserById (one of the methods inside UserController) in order to show the user his starting page. The best solution I could come up with so far is just adding User Id to the response header. Take a look at my OAuthAuthorizationServerProvider class, in GrantResourceOwnerCredentialsmethod I am adding User Id to the header, using context.Response.Headers.Add("User-Id", new string[] { "1" })
Here is the implementation of my OAuthAuthorizationServerProviderclass
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
//The actual credential check will be added later
if (context.UserName=="user" && context.Password=="user")
{
identity.AddClaim(new Claim("Id", "1"));
context.Validated(identity);
//Here I am adding User Id to the response header
context.Response.Headers.Add("User-Id", new string[] { "1" });
}
else
{
context.SetError("invalid_grant","The credentials provided are not valid");
return;
}
}
}
Basically the client then will have to read User-Id from the header. Is this a good solution I came up with or there is a better one? Also what if I want to pass the whole User object with all its properties to the response is it possible and how to do this?
Since you store the ID already in the claims, why don't you just decode your token on the client and read out the user id like that? Passing it through the headers could allow tampering with it (security).
Have a look on how you could achieve to decode your token and read the claims. This is a c# example https://contos.io/peeking-inside-your-jwt-tokens-using-c-bf6a729d06c8 but this could also be done even through javascript.
This, assuming you use the JWT-format as token (was not specified in your initial question).
Bad way to store UserID as a response header back to client side. This is a huge security concern.
The best way would be to store it as a Claims.
It is very easy to achieve this and get back the claims in the client side.
In your controller, call this and remember to reference
using Microsoft.AspNet.Identity;
var userId = User.Identity.GetUserId();
I'm using JWTAuth tokens for authentication. In the tests I've been passing the token up on each request after receiving it upon authentication, but when a few specific tests needed to be run on a set of different users in a loop the tests that worked for single users began to fail, which didn't make sense.
Through logging I can see that regardless that I'm invoking logout the service provider that I use on each request to get the authenticated user is only constructed on the first request and then appears to persist. So even though the user was logged out and a new user was logged in with and has a new token the service provider is not re-instantiated on each request like it is in the production application.
Is there a way to have the service provider not persist or be stored between requests? I noticed through debugging that I didn't even need to pass a token after login to the server on subsequent authenticated routes since it just reuses the persisted data in the service provider.
The example below works for any single user, but when iterated over multiple users the rule that a single user can't have two open applications throws and indicates the first user is still being used on the second iteration.
public function applicant_can_prequalify_if_age_of_majority_for_province_or_older()
{
// Arrange
$this->runPrequalifySeeders();
$provinces = Province::select('id', 'name', 'majority_age')->get();
$provinces->each(function ($province) {
// Act
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}#example.com";
$applicant = $this->createApplicant($username);
$this->login($applicant->username);
Log::info($applicant->username); // correct username: check
Log::info($this->token); // different token each time: check
// Applicants can only have one application at a time hence the
// new user for each iteration per province: fails on second
// iteration as service container persisted... I think
$application = $this->createNewApplication()->decodeResponseJson();
// Removed for brevity...
$this->logout();
});
}
UPDATE
In case it was related to the use of each I pasted the statements a couple times and shifted the province off the collection, and it still thinks in Act 2 that the user of Act 1 is authenticated.
// Act 1
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}#example.com";
$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();
// Removed for brevity
$this->logout();
// Act 2
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}#example.com";
$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();
// Removed for brevity
$this->logout();
UPDATE 2
Even though I've been logging out methods on the server that are being invoked as endpoints are hit like login, logout, etc. I wanted to check of logout was actually working and blacklisting the token, and it is based on this test that throws an Unauthenticated 401 error after the second create new application endpoint is hit. So it really does seem like the service provider isn't being cleared.
// Act
$province = $provinces->shift();
$provinceName = str_replace(' ', '', strtolower($province->name));
$username = "{$provinceName}#example.com";
$this->createApplicant($username)->login($username);
$application = $this->createNewApplication()->decodeJson();
$this->logout();
// 401 server response since token was successfully blacklisted
$application = $this->createNewApplication()->decodeJson();
So this appears to be due to the "subtlety of the framework" where the application instance is reused for all requests according to this Github issue.
Apparently you should be able to clear out the IoC by invoking $this->refreshApplication(), but when I do this it drops the database since I'm using an in memory database.
So instead I just delete the application on each iteration after assertions have been performed, which solves this particular issue.