User table encryption using aws kms in laravel - php

I have to encrypt user table data using aws kms encryption, i have refreed following code that doesnt work for me, throwing error 'The security token included in the request is invalid.
AWS HTTP error: Client error: POST https://kms.us-east-1.amazonaws.com resulted in a 400 Bad Request response:' can someone help me out?
my code is
<?php
use Aws\Kms\KmsClient;
// Somewhere in the controller or model
$this->load->config('aws');
// Not needed for EC2 instance role based authorization - for my local instance only
$key = $this->config->item('aws_s3_access_key');
$secret = $this->config->item('aws_s3_secret_key');
$orig = 'encrypt me please...';
$cryptic = 'CiD/AT9S0xQbpFXHDdw7Mq42htuEVj0vwvZzfR+9GRZCahKbAQEBAgB4/wE/UtMUG6RVxw3cOzKuNobbhFY9L8L2c30fvRkWQmoAAAByMHAGCSqGSIb3DQEHBqBjMGECAQAwXAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAxvNDVWPh6W4STdWakCARCAL/nzjIDZ8uQWAMWI1VBoNPt+TCe9qZMMbY1d1PnVjlJGa/BcVdAyN9KruzEOcFl6';
// Testing the encrypt and decrypt cycle
$kms = KmsClient::factory([
'credentials' => [
'key' => $key,
'secret' => $secret,
],
'region' => 'us-east-1',
]);
// Encrypt - should match $cryptic
$result = $kms->encrypt([
'KeyId' => 'alias/argus-db-crypt-local',
'Plaintext' => $orig,
]);
var_dump(base64_encode($result->get('CiphertextBlob')));
// Decrypt - should match $orig
$result = $kms->decrypt([
'CiphertextBlob' => base64_decode($cryptic),
]);
var_dump($result->get('Plaintext'));

You must be using an incorrect ACCESS/SECRET key pair.
Try to go to the security credentials on your account page: Click on your name in the top right corner -> My security credentials
Then generate access keys over there and use those access keys in your .env file

Related

Azure B2C with OpenID Connect and PKCE

I'm trying to make an Azure B2C login with Laravel, I'm halfway there right now.
The Laravel part is an API, and the client is in Angular. I've planned to make the whole process on the BE. When a user clicks "Sign in with Microsoft" to be redirected to the BE server-side page and I do the magic on the BE. In the end, I want to make a redirect to some FE page and set the access token in the cookie.
I've defined two web.php (server-side) routes. The first one is for redirecting away to the Azure login and the second one is for a callback.
Here is the code:
public function redirect()
{
$length = mt_rand(43, 128);
$bytes = random_bytes($length);
$codeVerifier = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
$state = Str::random(40);
$query = http_build_query([
'p' => 'B2C_1A_SIGNIN',
'client_id' => config('services.azureadb2c.client_id'),
'redirect_uri' => config('services.azureadb2c.redirect'),
'response_type' => 'code',
'scope' => 'openid',
'state' => $state,
'prompt' => 'login',
'code_challenge' => $this->generateCodeChallenge($codeVerifier),
'code_challenge_method' => 'S256',
]);
session(['state' => $state, 'code_verifier' => $codeVerifier]);
return redirect('https://' . config('services.azureadb2c.domain') . '.b2clogin.com/' . config('services.azureadb2c.domain') . '.onmicrosoft.com/b2c_1a_signin/oauth2/v2.0/authorize?' . $query);
}
public function callback(Request $request)
{
$client = new Client();
$response = $client->post("https://" . config('services.azureadb2c.domain') . ".b2clogin.com/" . config('services.azureadb2c.domain') . ".onmicrosoft.com/b2c_1a_signin/oauth2/v2.0/token?p=B2C_1A_SIGNIN", [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => config('services.azureadb2c.client_id'),
'client_secret' => config('services.azureadb2c.client_secret'),
'code' => $request->get('code'),
'redirect_uri' => config('services.azureadb2c.redirect'),
'code_verifier' => session('code_verifier'),
'scope' => 'openid',
],
]);
var_dump(json_decode($response->body()));
exit;
}
private function generateCodeChallenge($codeVerifier) {
$hash = hash('sha256', $codeVerifier, true);
$codeChallenge = rtrim(strtr(base64_encode($hash), '+/', '-_'), '=');
return $codeChallenge;
}
Here, I'm facing the next problem. Redirect is working well, the user is redirected properly and he can enter the credentials well and everything. At the end in the callback, I receive the authorization code, but when I make a POST request to get the access token I get the next error
`400 Bad Request` response: {"error":"invalid_grant","error_description":"AADB2C90085: The service has encountered an internal error. Please reauthe (truncated...)
Do you have any idea what can cause this error?
I tried to reproduce the same in my environment via Postman and got below results:
I registered one Azure AD B2C application by selecting SPA in redirect URIs like below:
I ran below c# code by Bac Hoang [MSFT] from this blog and got values of code_challenge and code_verifier successfully as below:
using IdentityModel;
using System.Security.Cryptography;
using System.Text;
namespace PKCEConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var random = new Random();
int cvlength = random.Next(43, 128);
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
var codeVerifier = new string (Enumerable.Repeat(chars, cvlength).Select(s => s[random.Next(s.Length)]).ToArray());
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var a = Encoding.UTF8.GetBytes(codeVerifier);
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64Url.Encode(challengeBytes);
}
Console.WriteLine("codeVerifier " + codeVerifier + "\n");
Console.WriteLine("codeChallenge " + codeChallenge + "\n");
Console.ReadLine();
}
}
}
Response:
Now, I ran below authorization request in browser and got code successfully like this:
https://sritenantb2c.b2clogin.com/sritenantb2c.onmicrosoft.com/B2C_1_Signin/oauth2/v2.0/authorize?
client_id=742978eb-ccb4-4b82-8140-3526417d4d09
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=openid
&code_challenge=l-kik2UPt2cInWJK6liasiRBhdpNm3qeOqUma_4Ih7E
&code_challenge_method=S256
Response:
When I tried to generate tokens via Postman with below parameters, I got only id_token and refresh_token without access token like below:
POST https://sritenantb2c.b2clogin.com/sritenantb2c.onmicrosoft.com/B2C_1_Signin/oauth2/v2.0/token
client_id:742978eb-ccb4-4b82-8140-xxxxxxxxxx
grant_type:authorization_code
scope:openid
code: <paste code from above step>
redirect_uri: https://jwt.ms
code_verifier:hMl0zyCvyUKJxS1gjOPqFNf1LPQ~tcvekFJeWsfzFGSFMi5~QkpCAq1QHMDT3N
Response:
To get access token, add new scope by exposing an API like below:
Now, add this scope in your application that will come under Delegated permissions like below:
Make sure to grant admin consent to the above scope like below:
Now, generate code again by running below authorization request in browser like this
https://sritenantb2c.b2clogin.com/sritenantb2c.onmicrosoft.com/B2C_1_Signin/oauth2/v2.0/authorize?
client_id=742978eb-ccb4-4b82-8140-xxxxxxxxxx
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=https://sritenantb2c.onmicrosoft.com/742978eb-ccb4-4b82-8140-xxxxxxxxxx/.default
&code_challenge=l-kik2UPt2cInWJK6liasiRBhdpNm3qeOqUma_4Ih7E
&code_challenge_method=S256
Response:
I generated access token successfully using below parameters via Postman like below:
POST https://sritenantb2c.b2clogin.com/sritenantb2c.onmicrosoft.com/B2C_1_Signin/oauth2/v2.0/token
client_id:742978eb-ccb4-4b82-8140-xxxxxxxxxx
grant_type:authorization_code
scope: https://sritenantb2c.onmicrosoft.com/742978eb-ccb4-4b82-8140-xxxxxxxxxxx/Admin.Read openid
code: <paste code from above step>
redirect_uri: https://jwt.ms
code_verifier:hMl0zyCvyUKJxS1gjOPqFNf1LPQ~tcvekFJeWsfzFGSFMi5~QkpCAq1QHMDT3N
Response:
When I decoded the above access token in jwt.ms, I got the scp claim like below:

get password client from access token passport laravel

i use passport in laravel and after user verification i want to generate a token for them with refresh token . for get refresh token i have to send curl request with password grant_type . after generate access token i want to get Password Grant Clientfrom database and pass it to my curl body . i fount this code snippet:
$tokenId = (new Parser(new JoseEncoder()))->parse($token)->claims()->get('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;
and the problem result of client variable is Personal Access Client not Password Grant Client and get this error because of its personal type:
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}
this is my code:
$token = $user->createToken(Config::get('auth.guards.api.token_name'))->accessToken;
$tokenId = (new Parser(new JoseEncoder()))->parse($token)->claims()->get('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;
$http = new \GuzzleHttp\Client();
try {
$response = $http->request('POST', url("/oauth/token"), [
'form_params' => [
'grant_type' => 'password',
'client_id' => $oClient->id,
'client_secret' => $oClient->secret,
'username' => $username,
'password' => $password,
'scope' => '*',
],
]);
} catch (\Exception $exception) {
}
how can i deal with this?
For obtaining the client_id and client_secret for Password Grant Client you need to run the following command on your authorization server (OAuth server) as stated here https://laravel.com/docs/9.x/passport#creating-a-password-grant-client
php artisan passport:client --password
The above command is not necessary to run if you already ran passport:install. The easiest way is to check your oauth_clients table for the column password_client there should be a row that has this value set to 1 (enabled).
It seems from your question that you are trying to obtain the client_id and client_secret programmatically from your client. This is not the correct way of doing it.
Basically after you run the above command to generate your client_id and client_secret you need to hard code them in your .env and use them in you CURL such as:
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'password',
'client_id' => env('OAUTH_CLIENT_ID'),
'client_secret' => env('OAUTH_CLIENT_SECRET'),
'username' => $username,
'password' => $password,
'scope' => '*',
]);
return $response->json();
You can obtain your client_id and client_secret from the oauth_clients table. Just make sure to copy the values where the password_client column is set to 1.
There should not be any security concern if your client is storing these credentials in the backend and doing the CURL from the backend.
In the case you are trying to do this from a mobile app and you might not have a way to securely store the client_id and client_secret. In this case you should not be using the Password Grant Client flow but instead the Authorization Code Grant with PKCE: https://laravel.com/docs/9.x/passport#code-grant-pkce

Outlook - OAuth 2 - Fail to retrieve access token, grant_type=password is unsupported, only authorization_code and refresh_token are

I'm currently trying to implement a way to synchronize my PHP App calendar with the Outlook calendar of my clients, using Azure API.
I use OAuth2 and the custom Microsoft provider by Steven Maguire.
I currently run in an issue where I get an error in my response :
{"error":"unsupported_grant_type","error_description":"The provided value for the input parameter 'grant_type' is not valid. Expected values are the following: 'authorization_code', 'refresh_token'."}
I'm having trouble understanding why the grant_type password is not supported, even though it says on the documentation of Azure that it is.
The request looks like this :
client_id=44bef79b-**********************&client_secret=H****************&redirect_uri=https%3A%2F%2F192.168.1.123%2Fmapeyral%2Fcalendarsync.php&grant_type=password&username=******************&password=***********&scope=openid%20profile%20offline_access%20Calendars.ReadWrite
The Authorize url used is : https://login.live.com/oauth20_token.srf
as defined in the Steven Maguire provider.
The header contains the content-type application/x-www-form-urlencoded (I've seen a lot of post where this was what caused the error).
Some of my code :
$this->provider = new Microsoft([
'clientId' => MicrosoftGraphConstants::CLIENT_ID,
'clientSecret' => MicrosoftGraphConstants::CLIENT_SECRET,
'redirectUri' => MicrosoftGraphConstants::REDIRECT_URI,
'urlAuthorize' => MicrosoftGraphConstants::AUTHORITY_URL . MicrosoftGraphConstants::AUTHORIZE_ENDPOINT,
'urlAccessToken' => MicrosoftGraphConstants::AUTHORITY_URL . MicrosoftGraphConstants::TOKEN_ENDPOINT,
'urlResourceOwnerDetails' => MicrosoftGraphConstants::RESOURCE_ID,
'scope' => MicrosoftGraphConstants::SCOPES
]);
if ($_SERVER['REQUEST_METHOD'] === 'GET' && !isset($_GET['code']))
{
// Try getting access token from Database
$workingAccount = $GLOBALS['AppUI']->getState('working_account');
if (isset($workingAccount))
{
// DB access
$DB = new DatabaseConnection();
$dbAccess = $DB->getConnection();
$contactData = DBUserUtils::getContactDataFromEmail($GLOBALS['AppUI']->getState('working_account'), $dbAccess);
// If at least one user contact found
if (!is_null($contactData))
{
// If has refresh token => fill session variables using refresh token
if (!is_null($contactData['contact_refreshToken']))
{
log_msg('debug.log', 'Has refresh token');
$GLOBALS['AppUI']->setState('preferred_username', $contactData['contact_email']);
$GLOBALS['AppUI']->setState('given_name', $contactData['contact_first_name']." ".$contactData['contact_last_name']);
// Get new tokens
$newAccessToken = $this->provider->getAccessToken('refresh_token', [
'refresh_token' => $contactData['contact_refreshToken']
]);
// Update tokens and DB
$GLOBALS['AppUI']->setState('refresh_token', $newAccessToken->getRefreshToken());
$GLOBALS['AppUI']->setState('access_token', $newAccessToken->getToken());
DBOAuthUtils::updateTokenForUser($contactData['contact_id'], $GLOBALS['AppUI']->getState('refresh_token'), $dbAccess);
$this->redirectTo($redirectURL);
}
else
{
$this->getAccessToken();
}
}
else
{
$this->getAccessToken();
}
}
else
{
$this->getAccessToken();
}
function getAccessToken(){
$accessToken = $this->provider->getAccessToken('password', [
'username' => '*************',
'password' => '********',
'scope' => MicrosoftGraphConstants::SCOPES
]);
}
During the first try it doesn't pass the if (isset($workingAccount)) condition (as expected) and go straight to the last else.
Code is a bit ugly for now but I don't think it has an impact on my problem.
Any help would be appreciated !
Thanks
Edit : added code
That helped me, the problem was that I need to use Azure Active Directory and not Azure AD 2.0.
Problem solved !

Firebase REST access using PHP with Authentication

I'm trying to access Firebase from a server using PHP, the Google Auth library, and a wrapper for Firebase's REST...This works great to accomplish that:
use Firebase\JWT\JWT;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use GuzzleHttp\Client;
$email = 'account#email.com';
$key = 'private_key_goes_here';
$scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/firebase.database',
];
$creds = [
'client_email' => $email,
'private_key' => $key,
];
$serviceAccount = new ServiceAccountCredentials($scopes, $creds);
$handler = HttpHandlerFactory::build(new Client());
$token = $serviceAccount->fetchAuthToken($handler);
$firebase = new \Firebase\FirebaseLib($url, $token);
$value = $firebase->get('test/hello');
# $value now stores "world"
However, this requires the security rules in Firebase to be universal read / write, which I do not want. If I update my security rules to this:
{
"rules": {
"test": {
".read": "auth != null"
}
}
}
The result in $value becomes {"error": "Permission denied"}. I've searched extensively, and tried numerous permutations and possible solutions, with no conclusive results.
I've used this code to provide JWT tokens to end clients, which can successfully use them and leverage the security rules with no problem. I initially tried that same approach for the server, but was unsuccessful. I opted to try to combine the two methods:
# Snipping code that didn't change...
$serviceAccount = new ServiceAccountCredentials($scopes, $creds);
$handler = HttpHandlerFactory::build(new Client());
$payload = [
'iss' => $email,
'sub' => $email,
'aud' => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
'iat' => time(),
'exp' => time() + 60 * 60,
'uid' => '123',
'claims' => [
'uid' => '123',
],
];
$payload = $serviceAccount->updateMetadata($payload);
$token = JWT::encode($payload, $key, 'RS256');
$firebase = new \Firebase\FirebaseLib($url, $token);
$value = $firebase->get('test/hello');
This seems to get close, but $value now contains {"error": "Missing claim 'kid' in auth header."}. To resolve this, I modified the encode call:
$token = JWT::encode($payload, $key, 'RS256', 'key_id_goes_here');
Which results in a slightly different error: Invalid claim 'kid' in auth header., suggesting I'm on the right track...But not quite there. Using the JWT token directly yields the exact same results. Any ideas what I'm doing wrong? The email, private key, and key id all came directly from the json credential file provided when I created the service account.
I've looked at dozens of pages of documentation and posts, here are the ones that were the most helpful:
Using JWT for Server Auth (Firebase Docs)
Using Custom Tokens to make REST requests to FB DB as an admin
Is it still possible to do server side verification of tokens in Firebase 3?
Cross posted to the Firebase Google Group.
You can specify an auth_variable_override query parameter when authenticating with a service account that will become the auth variable in the security rules. It should be a properly escaped JSON object. For example to do {"uid":123} you'd want to add:
?auth_variable_override=%7B%22uid%22%3A%22123%22%7D
to the end of your request URL.
Ultimately, the solution I ended up using was to switch PHP libraries. I initially dismissed this library because it is moving toward PHP7 only support, which I'm not ready to migrate to yet, but the current version (1.1) worked fine:
use Kreait\Firebase\Configuration;
use Kreait\Firebase\Firebase;
$clientId = '1234567890';
$email = 'account#email.com';
$key = 'private_key_goes_here';
$url = 'https://example.firebaseio.com';
$fbConfig = new Configuration();
$fbConfig->setAuthConfigFile([
'type' => 'service_account',
'client_id' => $clientId,
'client_email' => $email,
'private_key' => $key,
]);
$fb = new Firebase($url, $fbConfig);
$value = $fb->get('test/hello');
# $value now stores "world"

Unable to parse response body into XML: String could not be parsed as XML (View:

I have been tasked with connecting to an s3 bucket and using documentation have the following:
<?php
define('AWS_KEY', 'key in here');
define('AWS_SECRET_KEY', 'key in here');
define('HOST', 'https://console.aws.amazon.com/s3/home?region=us-east-1#');
use Aws\S3\S3Client;
// Establish connection with DreamObjects with an S3 client.
$client = S3Client::factory(array(
'base_url' => HOST,
'key' => AWS_KEY,
'secret' => AWS_SECRET_KEY
));
// list owned buckets
$blist = $client->listBuckets();
echo " Buckets belonging to " . $blist['Owner']['ID'] . ":\n";
foreach ($blist['Buckets'] as $b) {
echo "{$b['Name']}\t{$b['CreationDate']}\n";
}
// list Bucket contents
$o_iter = $client->getIterator('ListObjects', array(
'Bucket' => $bucketname
));
foreach ($o_iter as $o) {
echo "{$o['Key']}\t{$o['Size']}\t{$o['LastModified']}\n";
}
but I get the error in the title any ideas I have my access keys but i am confused about how to fix this issue ?
It is probably a bad idea to hardcode or to use environment variable to pass your secret key and access key.
A better design pattern would be to leverage EC2 Role or to use the SDK configuration file (see http://docs.aws.amazon.com/aws-sdk-php/guide/latest/credentials.html for details)
The base_url argument you're using is invalid, it is the URL of the console, not the one from the service. You can just ignore this parameter (as per http://docs.aws.amazon.com/aws-sdk-php/guide/latest/configuration.html#client-configuration-options), the SDK will build it automatically for you

Categories