chatbot: how to validate incoming requests from google hangouts - validation

We've put together a google hangouts chat bot to provide some convenient functionality for our team. The bot is of the 'bot URL' variety, meaning that hangouts sends requests to an app endpoint and our app responds appropriately. At the moment, we're struggling to now validate the incoming requests from google. Each request has a bearer token in the Authentication header, but that JWT token does not validate. Both the php client library [https://github.com/googleapis/google-api-php-client] and the online validator [https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=] return the error 'invalid signature'
The google client php library's Google_AccessToken_Verify class has a verifyIdToken method which we use as described here in this example [https://github.com/GoogleCloudPlatform/php-docs-samples/blob/master/auth/src/auth_cloud_explicit.php]. We pass the path of our service account key file and the project ID into the google client constructor. Then we pass the incoming request's bearer token into the verifyIdToken method.
use Google_Client;
// inside a laravel controller with $request in scope
$bearer_token = $request->bearerToken();
$keyPath = FILE_LOCATION
$client = new Google_Client([
'keyFilePath' => $keyPath,
'projectId' => GCP_CLIENT_ID
]);
$payload = $client->verifyIdToken($bearer_token);
if(!empty($payload)){
return $this->call(ParseGoogleChatRequest::class, [$request]);
}else{
\Log::debug('bad token');
}
I expect the google client library to be able to validate a google JWT. This github issue [https://github.com/firebase/php-jwt/issues/175] reflects our experience implementing this approach. I would like to get some general guidance on which approach we should be using.

I figured out an acceptable solution with the help of another SO question. The google client library was already importing firebase/php-jwt, so I followed along the same lines as Jed from the question I linked to. Extracting the KID from the token, I used it to identify the correct public key from this url. Then I instantiated the php-jwt library and called the decode method on it, passing the required arguments. The decode method also verifies the signature and returns the components of the JWT on success.

Related

JWT + Laravel Socialite with OAuth Parameters

What I want to achieve:
Safely allow users to connect their accounts to different social medias using a Single Page Application.
What I am doing:
I am using an SPA and therefor utilizing JWT as my user authentication method. I am passing the JWT token in the OAuth call with Laravel Socialite like this:
return Socialite::driver($provider)
->with(['provider' => $provider, 'token' => $token])
->redirectUrl($redirectUri)
->stateless()
->redirect();
On the callback I get the user based on the token. Using this method allows the third party provider to get access to the JWT token. Which is very unsafe.
My Question(s):
Is there any better way to do this? Should I use some kind of hash + salt + secret?
You should check the JWT.
JSON Web Tokens are an open, industry standard RFC 7519 method for
representing claims securely between two parties.
JWT Token composes of three parts, header, payload and verify signature.
You are using stateless authentication and the only way to authenticate the user is by the JWT Token. To authenticate the user after redirect, you should create a payload containing application's user id, and pass to the third party provider, so that when redirect, they will pass the JWT token back to you.
It is no problem to pass the JWT Token to third party provider, but be aware that the payload should not contain any sensitive data. If the payload is somehow sniffed, it will not have any harm because, if hacker is trying to change the payload, the verify signature helps and the application cannot verify the token and the application will throw exception.
The signature is used to verify that the sender of the JWT is who it
says it is and to ensure that the message wasn't changed along the
way.

Validating token in client application

I have an application which accepts JWTtoken and go through the claims and respond to the request. Once I receive the JWTtoken, I want to validate whether it is issued by the Identity server which I trust.
Any idea how an application can perform JWTtoken validation?
an application simply make call:
/connect/identitytokenvalidation?token=&client_id= and get the token validation done?
Do I need to create TokenClient instance to call RequestAssertionAsync? or I can simply make http get request by passing token value in the query string?
I can get the token value with the following way:
Request.GetOwinContext().Request.Headers["Authorization"];
Any sample would be of a great help.
If your endpoint is running in a Katana pipeline then you can use either the Microsoft JWT bearer authentication middleware, or you can use the IdentityServer3.AccessTokenValidation middleware. Either of these will be the proper validation.
If you don't want to use those and do it manually, then you can use the Microsoft JwtSecurityTokenHandler class to perform the validation.
Here's the relevant lines of code from our sample web api
https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/SampleAspNetWebApi/Startup.cs
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44333/core",
RequiredScopes = new[] { "write" },
});

How to exchange Google one-time authorization code for a refresh token without callback (intranet)?

I'm working on a intranet-based application and I want to use Google services. Currently I have successfully implemented Google Authentication with "Sign-In for Websites" using JavaScript client-side authentication. My users can now sign in or sign up with their Google accounts.
Now I want to use Google API to create and share Google Sheets with my users. These documents will be created with a specific Google account and then shared with my users.
This is why I want to use this server-slide flow to get a one-time authorization code and exchange it for a refresh token:
https://developers.google.com/identity/sign-in/web/server-side-flow
This refresh token will be stored in my database allowing me to user Google services on behalf of this offline user.
Using JavaScript library, I was able to get the one-time authorization code that I send to my server with a AJAX request.
auth2.grantOfflineAccess({'redirect_uri': 'postmessage'}).then(grantOfflineAccessCallback);
var grantOfflineAccessCallback = function(authResult) {
var auth_code = authResult.code;
// Exchange the one-time authorization code for tokens
$.post(...);
}
On server-side I use Google API PHP Client (v2.0.0-RC6) to acquire an access and refresh token.
$this->client = new Google_Client();
$this->client->setClientId($this->clientId);
$this->client->setClientSecret($this->clientSecret);
$this->client->setAccessType('offline');
$this->client->setApprovalPrompt('force');
$response = $this->client->fetchAccessTokenWithAuthCode($oneTimeCode);
I wasn't able to exchange the authorization code.
Client error: `POST https://www.googleapis.com/oauth2/v4/token` resulted in a `400 Bad Request` response:
{
"error": "invalid_request",
"error_description": "Missing parameter: redirect_uri"
}
On this page we can read:
On the server, exchange the auth code for access and refresh tokens.
Use the access token to call Google APIs on behalf of the user.
On the JAVA example code:
REDIRECT_URI: // Specify the same redirect URI that you use with your web
// app. If you don't have a web version of your app, you can
// specify an empty string.
Because the application I working on is an intranet application, I tried to specify an empty string for this redirect_uri parameter before calling fetchAccessTokenWithAuthCode() method:
$this->client->setRedirectUri('');
... result in Redirect URI must be absolute.
Can we use this hybrid server-slide flow without callback URL?
Is there any solution to my problem?
Thanks,
Edit:
redirect_uri is where the user will be redirected to after he signed in. This URL must be registered in the Google Project (developers console). So redirect_uri is NOT the callback...!
Problem is now solved with:
$this->client->setRedirectUri('http://same.url.as.in.developers.console/');

OAuth token parameter missing when using request_youtube API

I try to using youtube API in code ingniter and using librari from https://github.com/jimdoescode/CodeIgniter-YouTube-API-Library when i call request_youtube()from direct link the https://accounts.google.com/o/oauth1/auth sent massage like this :
400. That’s an error.
OAuth token parameter missing.
That’s all we know.
This the code of request_youtube() :
public function request_youtube()
{
$params['key'] = xxxxxxxxxxxx.apps.googleusercontent.com';
$params['secret'] = 'xxxxxxxxxxxx';
$params['algorithm'] = 'HMAC-SHA1';
$this->load->library('google_oauth', $params);
$data = $this->google_oauth->get_request_token(base_url().'index.php/example/access_youtube');
$this->session->set_userdata('token_secret', $data['token_secret']);
redirect($data['redirect']);
}
what wrong with my code...or any step i miss???
The library that you are using is deprecated probably because of which it is unable to make calls to the Google Servers and is returning 404 errors. You can still follow the original way of authentication using OAuth where you validate your app with the client id client secret and get the Auth code. With the Auth code making a POST request and getting a access token and a refresh token in exchange. For details please refer to this official Google Youtube Documentation on OAuth.

LinkedIn JS API token exchange to REST token using Spring Social for Linkedin

I'm trying to do the following:
Let the user authenticate and authorize through Linkedin using Linkedin JSAPI, then take the authentication details and send them to my server to get the user profile via server side communication.
I got the Linkedin button setup, got the authorization cookie all the way to my server (as described here), and was able to verify that the token is indeed signed correctly with my secret key.
Now I'm stuck at the point where I am supposed to take the token I got from JSAPI and exchange it for an access token.
This is the code I'm using, as mentioned it uses Spring Social for Linkedin, and it doesn't work as it throws a 401 Unauthorized response:
LinkedInConnectionFactory connectionFactory =
new LinkedInConnectionFactory(myLinkedinId, myLinkedinSecret);
OAuth1Operations oauthOperations = connectionFactory.getOAuthOperations();
AuthorizedRequestToken art = new AuthorizedRequestToken(new OAuthToken(codeIGotFromJSAPI, aSecretKey), whereDoIGetThisSignature);
OAuthToken accessGrant = oauthOperations.exchangeForAccessToken(art, null);
if (accessGrant == null) return null;
Connection<LinkedIn> connection = connectionFactory.createConnection(accessGrant);
if (connection != null) {
LinkedIn linkedin = connection.getApi();
return linkedin.profileOperations().getUserProfile();
}
What I'm actually confused about is the AuthorizedRequestToken object. The codeIGotFromJSAPI part is simple enough I think, it's just access_token, but what about aSecretKey, is it just my linkedin secret key? what about whereDoIGetThisSignature, how do I create that one? Do I use the same hash method as I used to validate the linkedin response and hash the access_token with my secret linkedin key? In the linkedin page, it says:
You need to pass four values as query parameters:
oauth_consumer_key, to identify yourself
xoauth_oauth2_access_token parameter, set to the value of the access_token field in the cookie.
signature_method set to HMAC-SHA1
signature, calculated as described in the OAuth 1.0a spec
So (1) is automatically done by the connection I suppose, (2) is the access token I provided, but how do I do (3) and (4)?
Lets suppose I get the following data in the JSAPI cookie set by Linkedin:
{
"signature_method":"HMAC-SHA1",
"signature_order": ["access_token", "member_id"],
"access_token":"AD2dpVe1tOclAsNYsCri4nOatfstw7ZnMzWP",
"signature":"73f948524c6d1c07b5c554f6fc62d824eac68fee",
"member_id":"vvUNSej47H"
"signature_version": 1
}
What do I need to do with it to go through the next step?
Use the following process:
Read the cookie
Transform "signature":"..." to &signature=...
Transform "signature_method":"HMAC-SHA1" to &signature_method=HMAC-SHA1
Transform "member_id":"..." to &oauth_customer_key=...
Transform "access_token":"..." to &xoauth_oauth2_access_token=...
Append all to the LinkedIn url plus ?
The LinkedIn JSAPI Token Exchange as described in Exchange JSAPI Tokens for REST API OAuth Tokens is currently not supported by Spring Social, according to a Spring forum discussion on this topic.
But there are implementation available to solve this task without Spring Social by using standard OAuth libraries available for Java. The LinkedIn user's access token, that you get from the exchange, can be put into a new AccessGrant object which can be used to create a Spring Social Connection<?> in the user's ConnectionRepository.
The code published in the LinkedIn developer forum discussion shows how to use Scribe to perform the exchange. The request that has to be sent to LinkedIn is a standard OAuth request but must ship the access_token field from the JSAPI token object as a HTTP query parameter xoauth_oauth2_access_token. The member_id that is also available to you is just for your information, and the signature allows you to verify both access_token and member_id without querying LinkedIn.

Resources