I am currently working on the project that I am going to integrate the application of my company and salesforce.
In my case, it seemed that using the JWT for authentication is better.
So, I wanted to try it.
but I don't know how to generate JWT on Ruby though I researched a lot.
So if anyone knows how to make jwt, I would love you to tell me how to do it.
What I wanted to do is that
1, create application on salesforce (done)
2, create X509 certification and set it on the application on salesforce. (done)
3, create JWT by using the secret key of X509 certification. (don't know how)
I have been looking for the way to solve this but I couldn't.
I guess I should be doing something like this.
require 'jwt'
payload = { data: 'test' }
# The secret must be a string. A JWT::DecodeError will be raised if it isn't provided.
hmac_secret = 'my$ecretK3y'
token = JWT.encode payload, hmac_secret, 'HS256'
# eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
puts token
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
# Array
# [
# {"data"=>"test"}, # payload
# {"alg"=>"HS256"} # header
# ]
puts decoded_token
but this doesn't work on my local.
When I run this file, it says
`require': cannot load such file -- jwt
I think PHP version of what I want to do is this.
<?php
require_once './vendor/autoload.php';
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
// ログインURL
// 本番: https://login.salesforce.com
// Sandbox: https://test.login.salesforce.com
// スクラッチ組織: https://test.saleforce.com
define('LOGIN_URL', 'https://test.salesforce.com');
// コンシューマ鍵
define('CLIENT_ID', <<接続アプリケーションのコンシューマ鍵>>);
// ユーザID
define('USER_ID', 'xxxxx#example.com');
function createjwt() {
$signer = new Sha256();
$privateKey = new Key('file://cert/server.key');
$time = time();
$token = (new Builder())->issuedBy(CLIENT_ID) // iss: コンシューマ鍵
->permittedFor(LOGIN_URL) // aud: SalesforceログインURL
->relatedTo(USER_ID) // sub: SalesforceユーザID
->expiresAt($time + 3 * 60) // exp: 3分以内
->getToken($signer, $privateKey);
return $token;
}
$jwt = createjwt();
echo $jwt;
If you have any clue please help me.
references are here:
https://help.salesforce.com/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm&type=5
https://github.com/jwt/ruby-jwt
It was not the error of JWT but that I couldn't install get properly.
I tried once because of shortage of memory.
and this solved this problem.
gem install jwt
thank you.
Related
I'm using the DocuSign ruby client (https://github.com/docusign/docusign-ruby-client) on ruby on rails to send a document via email to some clients, but when I deploy the project after 15 minutes the request between the application and DocuSign gets "paused". For some reason the gem creates the request but doesn't send it as you can see in the next image where I enable the debug in the gem:
In that point the log doesn't print any more after 15 minutes.
The code that send the message in my app is:
access_token = "xxxxxxx"
account_id = "xxxxxxxxx"
base_path = "xxxxxxxxxx"
envelope_args = {
signer_email: contact.email,
signer_name: contact.name,
template_id: document.docusign_id
}
#args = {
account_id: account_id,
base_path: base_path,
access_token: access_token,
envelope_args: envelope_args
}
envelope_args = #args[:envelope_args]
# 1. Create the envelope request object
envelope_definition = make_envelope(envelope_args)
# 2. Call Envelopes::create API method
# Exceptions will be caught by the calling function
envelope_api = create_envelope_api(#args)
envelope_api.create_envelope #args[:account_id], envelope_definition
I don't know what can I do.
Thank you
Your screenshot shows everything happening at 18:29:05 -- I don't understand the issue.
Also, have you tried install/using the RoR code example?
See if it has the same problem.
We looked for the issue in our code, but we saw that in the step which the request is send in the gem here always freezes. So we debugged there and we saw that curl was being used for ruby. At that point we saw that curl was trying to reconnect with Docusign but it wasn't success, so we found this issue in the version of curl that we had (https://github.com/curl/curl/issues/4499 )
To fix it we updated the version to the latest, and it fixed the issue.
Thanks for your answers.
I have an android app with Google sign-in. As per the documentation, I generated a token ID:
// Configure Google Sign-In with the requestIdToken
GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.server_client_id))
.requestEmail()
.build();
// Handle result
private void handleSignInResult(GoogleSignInResult result) {
if (result.isSuccess()) {
GoogleSignInAccount account = result.getSignInAccount();
String tokenId = account.getIdToken();
}
}
I'm facing the problem on the server side, with Ruby on Rails. I'm trying to use google-id-token gem. The README gives the following example:
validator = GoogleIDToken::Validator.new(expiry: 1800)
begin
payload = validator.check(token, required_audience, required_client_id)
email = payload['email']
rescue GoogleIDToken::ValidationError => e
report "Cannot validate: #{e}"
end
I have the token (from the android java code). What is required_audience? Should I use the same client_id of my client app? When I try to run the code on server, I'm getting payload as nil.
Also, I would like to know if this is the right way to verify the token ID.
After some research, I found answers to my own questions. Here are they:
What is required_audience?
It can be obtained from decoded JWT string. You can decode it as follows:
JWT.decode(token, nil, false)
Should I use the same client_id of my client app?
Yes. The required_audience and required_client_id should be same. Otherwise, verification fails
Then why was I getting payload as nil?
The problem is, the gem in GitHub and the one in RubyGems are different. I solved this problem by pointing Gemfile gem to GitHub:
gem 'google-id-token', git: 'https://github.com/google/google-id-token.git'
Google is deprecating the OpenID endpoint I was using (v1.0 I think, via the django_openid_auth module) and I need to update my app and migrate my users' accounts to use Google OAuth2.
I've changed the app to use python-social-auth and have it authenticating with social.backends.google.GoogleOAuth2 successfully.
I've written a pipeline function to find associated OpenID urls from the old table and this is working for the other backends I care about but Google:
def associate_legacy_user(backend, response, uid=None, user=None,
*args, **kwargs):
if uid and not user:
# Try to associate accounts registered in the old openid table
identity_url = None
if backend.name == 'google-oauth2':
# TODO: this isn't working
identity_url = response.get('open_id')
else:
# for all other backends, see if there is a claimed_id url
# matching the identity_url use identity_url instead of uid
# as uid may be the user's email or username
try:
identity_url = response.identity_url
except AttributeError:
identity_url = uid
if identity_url:
# raw sql as this is no longer an installed app
user_ids = sql_query.dbquery('SELECT user_id '
'FROM django_openid_auth_useropenid '
'WHERE claimed_id = %s',
(identity_url,))
if len(user_ids) == 1:
return {'user': User.objects.get(id=user_ids[0]['user_id'])}
As best I can tell from reading Google's migration guide, I need to add an openid.realm to the request, which I've done as follows in settings.py:
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS \
= {'openid.realm': 'http://example.com/'}
But this doesn't seem to be returning the open_id value in the response passed into my pipeline function.
I seem to be stuck on Step 3:
I tried sub-classing the backend to change the RESPONSE_TYPE to add id_token but that returned an empty response:
import social.backends.google
class CustomGoogleOAuth2(social.backends.google.GoogleOAuth2):
RESPONSE_TYPE = 'code id_token'
I tried building an additional request to https://www.googleapis.com/oauth2/v3/token similar to this example, but I don't really know how to go about putting that together and debugging it.
Some more details:
My old claimed_ids for Google OpenID users look like: https://www.google.com/accounts/o8/id?id=AItOawmAW18QuHDdn6PZzaiI5BWUb84mZzNB9eo
I'm happy to use social.backends.google.GoogleOpenIdConnect or a similar alternative backend if that's an easier solution. And while it seems to be closer to what the Google docs are talking about, I wasn't able to get it to work when I tried:
I get a 400 Error: invalid_request Parameter not allowed for this message type: nonce
I can get past the nonce error using social.backends.google.GoogleOpenIdConnect by adding id_token to the RESPONSE_TYPE but then I get an AuthMissingParameter error in my /complete/google-openidconnect/ endpoint as the request's GET and POST are empty. (Tried 'code id_token', 'token id_token', 'id_token', ...)
I don't want to use social.backends.google.GooglePlusAuth as that doesn't integrate as nicely with my current login form.
Worst case, I should be able to use social.pipeline.social_auth.associate_by_email, but I only have email addresses for maybe 80% of the users so that leaves quite a few who will have a new account and need support to associate it manually.
Try as I might, I can't find any examples of people doing a similar migration with python-social-auth, but it must be happening to lots of people.
Any ideas?
Solution works for python social auth 0.1.26
In new versions (0.2.*) of python social auth, there is GoogleOpenIdConnect, but it does not work fine (at least I did not succeed). And my project has some legacy, so I can't use new version of social.
I wrote custom GoogleOpenIdConnect backend:
import datetime
from calendar import timegm
from jwt import InvalidTokenError, decode as jwt_decode
from social.backends.google import GoogleOAuth2
from social.exceptions import AuthTokenError
class GoogleOpenIdConnect(GoogleOAuth2):
name = 'google-openidconnect'
ACCESS_TOKEN_URL = 'https://www.googleapis.com/oauth2/v3/token'
DEFAULT_SCOPE = ['openid']
EXTRA_DATA = ['id_token', 'refresh_token', ('sub', 'id')]
ID_TOKEN_ISSUER = "accounts.google.com"
def user_data(self, access_token, *args, **kwargs):
return self.get_json(
'https://www.googleapis.com/plus/v1/people/me/openIdConnect',
params={'access_token': access_token, 'alt': 'json'}
)
def get_user_id(self, details, response):
return response['sub']
def request_access_token(self, *args, **kwargs):
"""
Retrieve the access token. Also, validate the id_token and
store it (temporarily).
"""
response = self.get_json(*args, **kwargs)
response['id_token_parsed'] = self.validate_and_return_id_token(response['id_token'])
return response
def validate_and_return_id_token(self, id_token):
"""
Validates the id_token according to the steps at
http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation.
"""
try:
id_token = jwt_decode(id_token, verify=False)
except InvalidTokenError as err:
raise AuthTokenError(self, err)
# Verify the token was issued in the last 10 minutes
utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple())
if id_token['iat'] < (utc_timestamp - 600):
raise AuthTokenError(self, 'Incorrect id_token: iat')
return id_token
Notes:
get_user_id – An identifier for the user, unique among all Google accounts and never reused.
request_access_token – there is I add id_token_parsed to response, and it will be used in pipeline.
validate_and_return_id_token – validate of jwt is disabled, because in google developers console I have registered Client ID as web application so, I have no certificates for validate this data.
Then I created pipelines:
def social_user_google_backwards(strategy, uid, *args, **kwargs):
"""
Provide find user that was connect with google openID, but is logging with google oauth2
"""
result = social_user(strategy, uid, *args, **kwargs)
provider = strategy.backend.name
user = result.get('user')
if provider != 'google-openidconnect' or user is not None:
return result
openid_id = kwargs.get('response', {}).get('id_token_parsed', {}).get('openid_id')
if openid_id is None:
return result
social = _get_google_openid(strategy, openid_id)
if social is not None:
result.update({
'user': social.user,
'is_new': social.user is None,
'google_openid_social': social
})
return result
def _get_google_openid(strategy, openid_id):
social = strategy.storage.user.get_social_auth('openid', openid_id)
if social:
return social
return None
def associate_user(strategy, uid, user=None, social=None, *args, **kwargs):
result = social_associate_user(strategy, uid, user, social, *args, **kwargs)
google_openid_social = kwargs.pop('google_openid_social', None)
if google_openid_social is not None:
google_openid_social.delete()
return result
And changed my SOCIAL_AUTH_PIPELINE and AUTHENTICATION_BACKENDS settings:
AUTHENTICATION_BACKENDS = (
...
#'social.backends.open_id.OpenIdAuth' remove it
'social_extension.backends.google.GoogleOpenIdConnect', # add it
...
)
and
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
# 'social.pipeline.social_auth.social_user', remove it
'social_extension.pipeline.social_user_google_backwards', # add it
'social.pipeline.user.get_username',
...
# 'social.pipeline.social_auth.associate_user', remove it
'social_extension.pipeline.associate_user', # add it
'social.pipeline.social_auth.load_extra_data',
...
)
I am new to gerrit. I am using gerrit V. 2.6 . I want to use gerrit REST APIs in my python script. But not able to figure out how to use it. I tried below code but getting errors.
curl --digest --user user:password http://server/a/changes/path/to/project~branch~change_id/rebase
getting error :
401 Authorization Required
Authorization Required
This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.
Am I missing something.??
Are you using the correct username:password combination? This isn't your network password - it is the HTTP password that gerrit generates. You can find it by going to Settings->HTTP Password. If the password box is blank, click the button to have Gerrit generate a new password.
You may try using pygerrit. https://pypi.python.org/pypi/pygerrit/0.2.1
I think it has some APIs to easily access gerrit.
As #Ramraj mentioned, you can try using pygerrit or pygerrit2.
And I provide some examples that how I use gerrit REST APIs in my python script.
Here is the code.
auth = HTTPBasicAuth(username, password)
rest = GerritRestAPI(url='http://review.xxxxxx.com:8080', auth=auth)
Query changes by change number.
info = rest.get("/changes/?q=change:{}".format(change_number))
change_id = info[0]['change_id']
subject = info[0]['subject']
Query changes by commit id.
info = rest.get("/changes/?q=commit:{}".format(commit_id))
change_id = info[0]['change_id']
subject = info[0]['subject']
Revert a change.
headers = {'content-type': 'application/json'}
query = "/changes/" + str(change_number) + "/revert"
my_data = {"message": "{}".format("Revert "+str(subject))}
rest.post(query, data=json.dumps(my_data), timeout=30, headers=headers)
Review a change
headers = {'content-disposition': 'attachment', 'content-type': 'application/json'}
query = "/changes/" + str(change_number) + "/revisions/current/review"
my_data = { "labels": {"Code-Review": "+2", "Verified": "+1"} }
rest.post(query, data=json.dumps(my_data), timeout=30, headers=headers)
I have read this thoroughly: https://developers.google.com/google-apps/documents-list/#using_google_apps_administrative_access_to_impersonate_other_domain_users
I have googled this to death.
So far I have been able to:
Authorise with:
clientLogin
OAuth tokens (using my domain key)
retrieve document feeds for all users in the domain (authorised either way in #1)
I am using the "entry" from the feed to Export/Download documents and always get forbidden for other users for documents not shared with admin. The feed query I am using is like:
https://docs.google.com/feeds/userid#mydomain.com/private/full/?v=3
(I have tried with and without the ?v=3)
I have also tried adding the xoauth_requestor_id (which I have also seen in posts as xoauth_requestor), both on the uri, and as a client property: client.xoauth_requestor_id = ...
Code fragments:
Client Login (using administrator credentials):
client.http_client.debug = cfg.get('HTTPDEBUG')
client.ClientLogin( cfg.get('ADMINUSER'), cfg.get('ADMINPASS'), 'HOSTED' )
OAuth:
client.http_client.debug = cfg.get('HTTPDEBUG')
client.SetOAuthInputParameters( gdata.auth.OAuthSignatureMethod.HMAC_SHA1, cfg.get('DOMAIN'), cfg.get('APPS.SECRET') )
oatip = gdata.auth.OAuthInputParams( gdata.auth.OAuthSignatureMethod.HMAC_SHA1, cfg.get('DOMAIN'), cfg.get('APPS.SECRET') )
oat = gdata.auth.OAuthToken( scopes = cfg.get('APPS.%s.SCOPES' % section), oauth_input_params = oatip )
oat.set_token_string( cfg.get('APPS.%s.TOKEN' % section) )
client.current_token = oat
Once the feed is retrieved:
# pathname eg whatever.doc
client.Export(entry, pathname)
# have also tried
client.Export(entry, pathname, extra_params = { 'v': 3 } )
# and tried
client.Export(entry, pathname, extra_params = { 'v': 3, 'xoauth_requestor_id': 'admin#mydomain.com' } )
Any suggestions, or pointers as to what I am missing here?
Thanks
You were very close to having a correct implementation. In your example above, you had:
client.Export(entry, pathname, extra_params = { 'v': 3, 'xoauth_requestor_id': 'admin#mydomain.com' } )
xoauth_requestor_id must be set to the user you're impersonating. Also what you need is to use 2-Legged OAuth 1.0a with the xoauth_requestor_id set either in the token or in the client.
import gdata.docs.client
import gdata.gauth
import tempfile
# Replace with values from your Google Apps domain admin console
CONSUMER_KEY = ''
CONSUMER_SECRET = ''
# Set this to the user you're impersonating, NOT the admin user
username = 'userid#mydomain.com'
destination = tempfile.mkstemp()
token = gdata.gauth.TwoLeggedOAuthHmacToken(
consumer_key, consumer_secret, username)
# Setting xoauth_requestor_id in the DocsClient constructor is not required
# because we set it in the token above, but I'm showing it here in case your
# token is constructed via some other mechanism and you need another way to
# set xoauth_requestor_id.
client = gdata.docs.client.DocsClient(
auth_token=token, xoauth_requestor_id=username)
# Replace this with the resource your application needs
resource = client.GetAllResources()[0]
client.DownloadResource(resource, path)
print 'Downloaded %s to %s' % (resource.title.text, destination)
Here is the reference in the source code to the TwoLeggedOAuthHmacToken class:
http://code.google.com/p/gdata-python-client/source/browse/src/gdata/gauth.py#1062
And here are the references in the source code that provide the xoauth_requestor_id constructor parameter (read these in order):
http://code.google.com/p/gdata-python-client/source/browse/src/atom/client.py#42
http://code.google.com/p/gdata-python-client/source/browse/src/atom/client.py#179
http://code.google.com/p/gdata-python-client/source/browse/src/gdata/client.py#136