I've implemented in my backend Cognito with Signup and Login, MFA activation and inactivation, but now I want to implement the remember devices, to reduce SMS confirmation.
For that, I've adjusted the InitiateAuth Function to the following code:
$client->initiateAuth([
'AuthFlow' => 'USER_SRP_AUTH', // REQUIRED
'AuthParameters' => [
"USERNAME" => $email,
"PASSWORD" => $password,
"SRP_A" => $bigA,
],
'ClientId' => $this->getClientId(), // REQUIRED
]);
This function runs properly, and returns the code in following image:
https://i.gyazo.com/a439e48e2de85a094f56ed4cfee10f83.png
Then, I continue generating SRP Values, and call in the function respondToAuthChallenge, with the following code:
$client->respondToAuthChallenge([
'ChallengeName' => 'DEVICE_SRP_AUTH',
'ChallengeResponses' => [
'USERNAME' => $username,
'SRP_A' => $bigA,
],
'ClientId' => $this->getClientId(),
]);
Yet, It returns me an error saying: 'Missing required parameter DEVICE_KEY'.
If I put a DEVICE_KEY key inside ChallengeResponses it starts returning me the error 'Device does not exist.'
I've searched a lot and cannot find a way to generate the DEVICE_KEY. I've tried with unique ID and sending it in both initiateAuthand respondToAuthChallenge but the error is the same.
Any clue how can I do it? I Believe that SRP code is not 100% yet, as still understanding the concept, yet, cannot understand the DEVICE_KEY part.
Thanks
It looks like you have to use Server Side Authentication Flow
For server-side apps, user pool authentication is similar to that for client-side apps, except:
The server-side app calls the AdminInitiateAuth API (instead of InitiateAuth). This method requires AWS admin credentials. This method returns the authentication parameters.
Once it has the authentication parameters, the app calls the AdminRespondToAuthChallenge API (instead of RespondToAuthChallenge), which also requires AWS admin credentials.
The AdminInitiateAuth returns among other stuff the device key.
Related
So I'm trying to write some unit/feature tests for an implementation of Passport on Lumen for our new authentication server. However I'm running into so many annoying issues I'm not even sure I'm going about doing this the right way.
The way passport is currently set up in Lumen, for web requests especially, we fake internal requests to Passport and store the client id and secret in an env file on the backend. (The client secret is hardcoded in .env so it will always be created with the same ID and secret via seeder on each dev machine) Here is the code for that:
class PassportOauth
{
const OAUTH_LOGIN = '/api/v1/oauth/token';
/**
* Functions to encapsulate the token request (login) needs
* */
public static function login($username, $password)
{
return self::clientLogin($username, $password, env('CLIENT_ID'), env('CLIENT_SECRET'));
}
public static function clientLogin($username, $password, $clientID, $clientSecret)
{
return self::post(self::OAUTH_LOGIN, [
'grant_type' => 'password',
'client_id' => $clientID,
'client_secret' => $clientSecret,
'username' => $username,
'password' => $password
]);
}
private static function post($url, $data)
{
return app()->handle(Request::create($url, 'POST', $data));
}
}
Anyways, this class works great in our dev environment. It handles requests and hands out a access token / refresh token like it's supposed to.
However, when I run unit tests testing out this class, I keep getting the error "grant type not supported," despite the password client existing in the unit test db and all the environment variables being present.
Thus, I have switched to instead of testing out the PassportOauth class, to using $this->call() in unit tests for faking calls to Passport:
private function doLogin($email, $password)
{
return json_decode($this->call('POST', self::OAUTH_LOGIN, [
'grant_type' => 'password',
'client_id' => env('CLIENT_ID'),
'client_secret' => env('CLIENT_SECRET'),
'username' => $email,
'password' => $password
])->getContent());
}
this works great on development! Unit tests pass and everything. However, when they run in our ci/cd pipeline and it runs unit tests, I get the error that the oauth public/private keys are not installed. This is pretty obvious, I just need to run passport:install right?
Problem is, the passport db tables aren't created in the pipeline at that moment. And I'm not sure I want to add creating the db migrations each time a ci/cd pipeline is run.
So my questions here are:
1) Am I even approaching this the right way? If I am, how do I get around the oauth private/public keys that I don't even need, because I've statically created the password client from hardcoded values in my env file?
2) Is there a better approach to unit testing this? So far my approach has given me a lot of grief.
yes, the way to test this is by not trying to unit test it. What you want is integration testing.
You would call the authentication server directly, like a normal client, you'd get a token then you start doing the other tests, what happens when the token is fine, what happens when it expires, what happens when you have the wrong token for your resource server etc.
To be clear, We have created the EC2 policy, so my site can directly access the services like Parameter store, S3, Amazon SES etc.
As of now, all of my credentials are stored on AWS Parameter Store and then site is using those credentials i.e. DB credentials, diff. API keys etc. So only hard coded credentials are the one which fetch the parameters from Parameter Store. Now client want to remove those hard coded credentials as well, that's why we have created the EC2 Policy.
Till now, we have code like below to fetch the parameters:
$config = array(
'version' => 'latest',
'region' => '*****',
'credentials' => array(
'key' => '*******',
'secret' => '******',
)
);
$s3_instance = new \Aws\Ssm\SsmClient($config);
$result = $s3_instance->getParameters([
'Names' => $credential_group,
'WithDecryption' => true
]);
//converting S3 private data to array to read
$keys = $result->toArray();
var_dump($keys);
Now the question is what i have to change in above code, so it should work without passing those credentials.
Note: I am using AWS PHP library to perform above.
Update
Further reading the documentation, https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html
Using Credentials from Environment Variables
If you don't provide credentials to a client object at the time of its instantiation, the SDK attempts to find credentials in your environment. The first place the SDK checks for credentials is in your environment variables. The SDK uses the getenv() function function to look for the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables. These credentials are referred to as environment credentials.
So after that, i have tried the below:
var_dump(getenv('AWS_ACCESS_KEY_ID'));
But it returns the bool(false). So does i need to manually setup those in environment credentials?
Which things i need to change in above code?
Update
Based on this doc: https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#credentials
I had made below change (Removed the credentials part from array):
$config = array(
'version' => 'latest',
'region' => '*****'
);
Then system throws the below warnings:
Warning: include(Test_Role_Cognitoaccess_from_instanceRole.php): failed to open stream
Warning: include(): Failed opening 'Test_Role_Cognitoaccess_from_instanceRole.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php')
Warning: include(Test_Role_Cognitoaccess_from_instanceRole.php): failed to open stream
Warning: include(): Failed opening 'Test_Role_Cognitoaccess_from_instanceRole.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php')
As you already mentioned that you attached the policy to EC2 IAM role to access other AWS services.
You should try to create a default credential provider, this will automatically pick keys from the role.
$provider = CredentialProvider::chain(CredentialProvider::env(), CredentialProvider::ini(), CredentialProvider::instanceProfile(), CredentialProvider::ecsCredentials());
When you pass credentials directly to SsmClient and same time you have defined a role to the EC2 machine then you are making confusion for the AWS. If you have defined the permission for the EC2 instance then just do as follow:
use Aws\Ssm\SsmClient;
$client = new SsmClient(['version' => 'latest', 'region' => 'ap-southeast-2']);
$result = $client->getParameters(['Names' => ['My-SECRATE-KEY'], 'WithDecryption' => true]);
print_r($result);
Please keep in mind that permissions take a little time to propagate and in this period you will get permission error for the specific user. If you wait and let the changes take effect then mentioned code will work without any error. In my case I attached AmazonSSMReadOnlyAccess to the EC2 role and to EC2 instance. If you key/value in Parameter store is not encrypted then you can remove 'WithDecryption' => true or change it to false.
I'm connecting to Amazon SES via this php-code
$ses = new SesClient([
'credentials' => [
'key' => KEY,
'secret' => SECRET_KEY,
],
'region' => REGION,
'version' => SES_VERSION,
]);
How can I recognize here, whether constants KEY and SECRET_KEY are valid or invalid (such as wrong, inputed with typos and so on) ?
Is there any method in AWS SDK to verify it ?
I use the Python call get_user(). With no arguments, this call will return the user name based on the access key ID. This validates that the credentials are correct. This technique is not bulletproof, but does provide a simple, quick method. You can test this concept with the CLI aws iam get-user.
Python IAM get_user()
I'm using Hybridauth 3 in my PHP app to make some periodical tweets on behalf of my account.
The app has all possible permissions. I'm giving it all permissions when it asks for them on the first auth step.
After that Twitter redirects me to the specified callback URL and there I'm getting a pair of access_token and access_token_secret.
But when I'm trying to make a tweet using these tokens - it gives me:
{"errors":[{"code":220,"message":"Your credentials do not allow access to this resource."}]}
Here's how I'm trying to make a tweet:
$config = [
'authentication_parameters' => [
//Location where to redirect users once they authenticate
'callback' => 'https://mysite/twittercallback/',
//Twitter application credentials
'keys' => [
'key' => 'xxx',
'secret' => 'yyy'
],
'authorize' => true
]
];
$adapter = new Hybridauth\Provider\Twitter($config['authentication_parameters']);
//Attempt to authenticate the user
$adapter->setAccessToken(/*tokens I've got from getAccessToken() on /twittercallback/*/);
if(! $adapter->isConnected()) {
// never goes here, so adapter is connected
return null;
}
try{
$response = $adapter->setUserStatus('Hello world!');
}
catch (\Exception $e) {
// here I've got the error
echo $e->getMessage();
return;
}
Tried to recreate tokens and key\secret pairs and passed auth process for the app many times, including entering password for my Twitter account (as suggested in some posts on stackoverflow) but still have this error.
P.S. According to this, Hybridauth has fixed the issue in the recent release.
It looks like you are using application authentication as opposed to user authentication. In order to post a tweet, you must authenticate as a user. Also, make sure your Twitter app has read/write privileges.
After comparing headers of outgoing requests from my server with the ones required by Twitter, I've noticed that Hybris doesn't add very important part of the header: oauth_token. At least it's not doing this in the code for Twitter adapter and for the scenario when you apply access token with setAccessToken(). It's just storing tokens in the inner storage but not initializing corresponding class member called consumerToken in OAuth1 class.
So to initialize the consumer token properly I've overridden the apiRequest method for Twitter class (before it used the defalut parent implementation) and added a small condition, so when consumer token is empty before the request - we need to try to init it.
public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [])
{
if(empty($this->consumerToken)) {
$this->initialize();
}
return parent::apiRequest($url, $method, $parameters, $headers);
}
I'm not sure that I've fixed it the best way, but as long as it's working - that's fine.
For your info setAccessToken was fixed in v3.0.0-beta.2 (see PR https://github.com/hybridauth/hybridauth/pull/880)
I faced the same error when implementing a sample app in clojure and the following resource was a huge help to sort out my confusion about application-only auth vs user authentication: https://developer.twitter.com/en/docs/basics/authentication/overview/oauth
I am trying to use pubnub with their access manager to authorize certain users to a specific channel and then publish a message to the channel after the user has been granted read/write rights. I must be doing something wrong with the publish() call on this after granting access to the user. The first part of code below returns what looks like a successful response for the grant() but the publish() call results in :
Fatal error: Call to undefined method access::publish() in /home/dayfv98/public_html/mobile/pubtest.php on line 48
Here is my code:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require('pubnub.php');
require('pam.php');
$chat_entry = addslashes(trim($_POST['chat_entry']));
// CONNECT TO ACCESS MANAGER
$manager = new access(
"MY_PUB_KEY...not included for security",
"MY_SUB_KEY...not included for security",
"MY_SEC_KEY...not included for security"
);
## Grant User Access
print_r($manager->grant(
"chat", // CHANNEL
"44444", // STRING (AUTH KEY)
true, // READ
true, // WRITE
0 // TTL in MINUTES
));
$manager->publish(array(
'channel' => 'chat', ## REQUIRED Channel to Send
'message' => $chat_entry
));
?>
The access manager library is not intended to be used to do anything outside of the scope of the access manager. It is only intended for grants, revokes, and audits.
You will need to use the normal PubNub sdk, but when constructing your PubNub object, provide the auth token you have previously granted.
**EDIT : ** The PHP SDK seems to have some issue with Access Manager right now. I've done some minor hotfix modifications to address this and have included them into a gist, but please keep an eye on the official repository on github for a newer version : https://gist.github.com/keyosk/9c86b981948a3cf7f378
For instance, to use the auth token you granted in your example above, you would do this :
$pubnub = new Pubnub(array(
'publish_key' => 'MY_PUB_KEY',
'subscribe_key' => 'MY_SUB_KEY',
'auth_token' => '44444',
));
$pubnub->publish(array(
'channel' => 'chat',
'message' => $chat_entry
));