How to create laravel passport refresh token - oauth-2.0

How to create refreshingtoken using laravel passport.I create access token when login $user->createToken('Laravel')->accessToken.After login Refreshingtoken not working how to create

If your application issues short-lived access tokens, users will need to refresh their access tokens via the refresh token that was provided to them when the access token was issued:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://YOURSITE.COM/oauth/token', [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
]);
return $response->json();
This /oauth/token route will return a JSON response containing access_token, refresh_token, and expires_in attributes. The expires_in attribute contains the number of seconds until the access token expires.
Taken from: https://laravel.com/docs/9.x/passport#refreshing-tokens

Related

Firebase authentication with custom token

I have a firebase project which Im trying to authenticate from my rails server creating a custom token with the library ruby-jwt as it says on the docs, but i keep getting the same error:
auth/invalid-custom-token, The custom token format is incorrect. Please check the documentation.
The credentials.json is from the service account I made in google console, uid is sent from the front end to the api.
def generate_auth_token(uid)
now_seconds = Time.now.to_i
credentials = JSON.parse(File.read("credentials.json"))
private_key = OpenSSL::PKey::RSA.new credentials["private_key"]
payload = {
:iss => credentials["client_email"],
:sub => credentials["client_email"],
:aud => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
:iat => now_seconds,
:exp => now_seconds+(60*60), # Maximum expiration time is one hour
:uid => uid.to_s,
:claims => {:premium_account => true}
}
JWT.encode(payload, private_key, 'RS256')
end
it looks like this in jwt.io
{
"iss": "defered#defered.iam.gserviceaccount.com",
"sub": "defered#defered.iam.gserviceaccount.com",
"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat": 1486824545,
"exp": 1486828145,
"uid": "4",
"claims": {
"premium_account": true
}
}
It looks like the accepted answer found a way to link authentication from Firebase to Rails, but the original question seems to be asking how to link Rails authentication to Firebase (which is what I was trying to do).
To keep your authentication logic in Rails (ex: from Devise) and share it with Firebase, first get a Firebase server key as a .json file from your Service Accounts page in your project's settings.
You'll only need the private_key and client_id from this file, which I recommend storing as environment variables so they're not potentially leaked in source code.
Next, make a Plain ol' Ruby object (PORO) that will take in a User and spit out a JSON Web Token (JWT) that Firebase can understand:
class FirebaseToken
def self.create_from_user(user)
service_account_email = ENV["FIREBASE_CLIENT_EMAIL"]
private_key = OpenSSL::PKey::RSA.new ENV["FIREBASE_PRIVATE_KEY"]
claims = {
isCool: "oh yeah"
}
now_seconds = Time.now.to_i
payload = {
iss: service_account_email,
sub: service_account_email,
aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
iat: now_seconds,
exp: now_seconds + (60*60), # Maximum expiration time is one hour
uid: user.id,
# a hash to pass to the client as JSON
claims: claims
}
JWT.encode payload, private_key, "RS256"
end
end
Now send this JWT to authenticated users through javascript in your application layout:
window.firebaseJWT = "#{FirebaseToken.create_from_user(current_user)}";
In your frontend code, you can now use this token to authenticate users:
firebase
.auth()
.signInWithCustomToken(window.firebaseJWT)
.catch(error => {
console.error(error);
});
Remember to sign them out of firebase when they sign out of your application:
firebase
.auth()
.signOut()
.then(() => {
// Sign-out successful.
})
.catch(error => {
console.error(error);
});
I found a better way to authenticate, I'm just sending the token that firebase gives you and verifying it on rails with the information I need and that's it.
Check if your secret key is wrapped in double quotes and not single as they contain '\n' escape sequences. An auth/invalid-custom-token error is thrown if the secret key is not as specified in the documentation.

Can I send requests to app with devise without credentials?

I have backend app with devise and a ionic app from which I make requests to backend.
Whenever I send requests, gets me 401 status unauthorized.
In this project I've already doorkeeper to manage authorization, so I don't rly need authorization from devise. Can I somehow make these requests( add something to headers ) always authorized without sending post request with credentials?
You need to identify the user somehow. If you're not using a session, you'll need to generate some kind of access token, which Doorkeeper can do for you. I'm not sure if this is helpful or not, but I set up the following flow recently.
One option when using OAuth2 through a trusted client, e.g. a front-end app you build/distribute yourself, is the Resource Owner Password Credentials Grant. Doorkeeper has a guide in the docs for this with advice on dealing with Devise.
I ended up with something like this in my Doorkeeper initializer. You don't need to authorize the client, because you trust it:
resource_owner_from_credentials do |routes|
request.params[:user] = {:email => request.params[:email], :password => request.params[:password]}
request.env['devise.allow_params_authentication'] = true
request.env['warden'].authenticate!(:scope => :user)
end
skip_authorization do |resource_owner|
true
end
Then you should be able to send a request using the password grant type as follows (also shown in the docs).
RestClient.post 'http://localhost:3000/oauth/token', {grant_type: 'password', email: 'email#gmail.com', password: 'password'}, {:accept => 'application/json'}
You should receive the same JSON back as shown in the docs.
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}
Now you have a way of identifying your user. You just attach this token to each request to endpoints protected by doorkeeper_for.
RestClient.get 'http://localhost/api/v1/users', { 'Authorization' => 'Bearer 1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa', :accept => 'application/json'}

Google_AuthException [ 400 ]: Error fetching OAuth2 access token, message: 'invalid_grant'

The problem arises when I get the code and makes a request for a token. For authorization using code that works fine on other projects. I check Credentials in developer console and my config file. Attempts to connect the authorization from other sites and other accounts yielded nothing. I checked redirects, headers, but found nothing.
$request = Google_Client::$io->makeRequest(new Google_HttpRequest('https://accounts.google.com/o/oauth2/token', 'POST', array(), array(
'code' => $_GET['code'],
'grant_type' => 'authorization_code',
'redirect_uri' => $this->redirectUri,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
)));
This request return "Error fetching OAuth2 access token, message: 'invalid_grant'"

Rails Google Client API - unable to exchange a refresh token for access token

After struggling with some SSL issues on my machine, I'm still trying to access a user's Blogger account through the Google Ruby Client API. I'm using the following:
Rails 3.2.3
Ruby 1.9.3
oauth2 (0.8.0)
omniauth (1.1.1)
omniauth-google-oauth2 (0.1.13)
google-api-client (0.4.6)
I can successfully authenticate users and access their blogs through the Google API at the time of authentication. When a user logs in, I store the access_token and refresh_token I receive from Google. and everything works great until the access_token expires. I'm trying to build the functionality that exchanges the refresh_token for a new access_token, but keep coming up against walls. Using the client documentation as an example, this is the code I'm using:
client = Google::APIClient.new
token_pair = auth.oauth_token # access_token and refresh_token received during authentication
# Load the access token if it's available
if token_pair
client.authorization.update_token!(token_pair.to_hash)
end
# Update access token if expired
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
blogger = client.discovered_api('blogger', 'v3')
result = client.execute(
api_method: blogger.blogs.list_by_user,
parameters: {'userId' => "self", 'fields' => 'items(description,id,name,url)'},
headers: {'Content-Type' => 'application/json'})
This code works perfectly while the access_token is valid. As soon as it expires though, I'm seeing 2 problems:
Even though I know the token is expired (I've checked expires_at value in the database), client.authorization.expired? returns false -- is there a different way I can check the expiration of the token besides using the value in the database?
When I force the execution of client.authorization.fetch_access_token! I get an invalid_request error.
Can someone please let me know how I can exchange a refresh_token for a new access_token using the client API? Even if you know how to do it in another language, that would be a big help as I can then try to Rubyfy it. Thanks!!
You may have already found this, but you can read through the whole process here at google: https://developers.google.com/accounts/docs/OAuth2WebServer
The omniauth-google-oauth2 strategy already takes care of setting the access_type and approval_prompt so getting a refresh token is just a matter of posting to https://accounts.google.com/o/oauth2/token with grant_type=request_token
Here is roughly the code I use:
def refresh_token
data = {
:client_id => GOOGLE_KEY,
:client_secret => GOOGLE_SECRET,
:refresh_token => REFRESH_TOKEN,
:grant_type => "refresh_token"
}
#response = ActiveSupport::JSON.decode(RestClient.post "https://accounts.google.com/o/oauth2/token", data)
if #response["access_token"].present?
# Save your token
else
# No Token
end
rescue RestClient::BadRequest => e
# Bad request
rescue
# Something else bad happened
end
Since you are using the Ruby Google API Client, why not use it to exchange the refresh token as well? The Ruby API does pretty much the same thing internally, which #brimil01 has said in his answer.
This is how I use the Ruby API to exchange my refresh token for a new access token.
def self.exchange_refresh_token( refresh_token )
client = Google::APIClient.new
client.authorization.client_id = CLIENT_ID
client.authorization.client_secret = CLIENT_SECRET
client.authorization.grant_type = 'refresh_token'
client.authorization.refresh_token = refresh_token
client.authorization.fetch_access_token!
client.authorization
end
And according to this issue here, it is recommended not to use the expired? method to check if an access token has expired.
Basically, don't call the expired? method. There are essentially zero
scenarios where that's a good idea. It simply won't give you reliable
expiration information. It's more of a hint than a real expiration
timestamp, and the token server may decide to honor an expired token
anyways in certain somewhat theoretical, but important, circumstances.
If you do get an invalid grant error, always refresh your access token
and retry once. If you're still getting an error, raise the error.
Here is what I do.
# Retrieved stored credentials for the provided user email address.
#
# #param [String] email_address
# User's email address.
# #return [Signet::OAuth2::Client]
# Stored OAuth 2.0 credentials if found, nil otherwise.
def self.get_stored_credentials(email_address)
hash = Thread.current['google_access_token']
return nil if hash.blank?
hash[email_address]
end
##
# Store OAuth 2.0 credentials in the application's database.
#
# #param [String] user_id
# User's ID.
# #param [Signet::OAuth2::Client] credentials
# OAuth 2.0 credentials to store.
def self.store_credentials(email_address, credentials)
Thread.current['google_access_token'] ||= {}
Thread.current['google_access_token'][email_address] = credentials
end
def self.credentials_expired?( credentials )
client = Google::APIClient.new
client.authorization = credentials
oauth2 = client.discovered_api('oauth2', 'v2')
result = client.execute!(:api_method => oauth2.userinfo.get)
(result.status != 200)
end
# #return [Signet::OAuth2::Client]
# OAuth 2.0 credentials containing an access and refresh token.
def self.get_credentials
email_address = ''
# Check if a valid access_token is already available.
credentials = get_stored_credentials( email_address )
# If not available, exchange the refresh_token to obtain a new access_token.
if credentials.blank?
credentials = exchange_refresh_token(REFRESH_TOKEN)
store_credentials(email_address, credentials)
else
are_credentials_expired = credentials_expired?(credentials)
if are_credentials_expired
credentials = exchange_refresh_token(REFRESH_TOKEN)
store_credentials(email_address, credentials)
end
end
credentials
end
I fixed it with simple code below.
def refesh_auth_tooken(refresh_token)
client = Google::APIClient.new
puts "REFESH TOOKEN"
client.authorization = client_secrets
client.authorization.refresh_token = refresh_token
#puts YAML::dump(client.authorization)
client.authorization.fetch_access_token!
return client.authorization
end

Failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request - OAuth 2.0 POST

I am working with YouTube APIs for my college project, and I keep getting an error. Here I send them to the authorisation page to log in, when they allow access it sends the $_GET['code'] string back. Then I send this along with some other data and it should send back a JSON object. Instead I am just getting
Warning: file_get_contents(https://accounts.google.com/o/oauth2/token)
[function.file-get-contents]: failed to open stream: HTTP request
failed! HTTP/1.0 400 Bad Request in http://www.example.net/callback.php on line 27
I have replaced my domain with example.net just for security
urlencode($_GET['code']),
'client_id' => urlencode('111522767640.apps.googleusercontent.com '),
'client_secret' => urlencode('secret'),
'redirect_uri' => urlencode('http://example.net/callback.php'),
'grant_type' => urlencode('authorization_code')
)
);
$params =
array('http' =>
array(
'method' => 'POST /o/oauth2/token HTTP/1.1',
'header' => 'Host: accounts.google.com\r\n'.
'Content-Type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($params);
$result = file_get_contents('https://accounts.google.com/o/oauth2/token', false,$context);
var_dump($_SESSION);
var_dump($result);
}
else //If code isnt set, user must have come here erroniously or has denied access to this program
{
//header( 'Location: www.example.net/error.php' ) ;
}
?>
file_get_contents is going to make a GET request to the url specified, but oauth2/token needs a POST request.
See reference Google OAuth2, PHP HTTP.
if you are using oauth2, goto libraries/oauth2/provider.php and uncomment the code line 182 shows
$ci = get_instance();
$ci->load->spark('curl/1.2.1');
$ci->curl
->create($url)
->post($params, array('failonerror' => false));
$response = $ci->curl->execute();
Here's how to do the Google oAuth properly:
$config = (object) array(
'CLIENT_ID' => 'AAAAA',
'CLIENT_SECRET' => 'BBBBB',
'REFRESH_TOKEN' => 'CCCCC',
);
$sJSON = file_get_contents('https://www.googleapis.com/oauth2/v4/token',FALSE,
stream_context_create(array('http'=>array(
'ignore_errors' => TRUE, // see errors in response instead of empty on error
'method' => 'POST',
'header' => array(
'Content-Type: application/x-www-form-urlencoded'
),
'content' => http_build_query(array(
'grant_type' => 'refresh_token',
'client_id' => $config->CLIENT_ID,
'client_secret' => $config->CLIENT_SECRET,
'refresh_token' => $config->REFRESH_TOKEN
))
)))
);
You can then use json_decode() to parse $sJSON into an object and then retrieve the access_token property.
For those who are wondering how to get the CLIENT_ID, CLIENT_SECRET, and REFRESH_TOKEN, watch this video. It's not easy. In my case, I needed to do this for Google Adwords API. So, I had to get my Developer Token and Login Customer ID from https://ads.google.com/home/tools/manager-accounts. Then, I had to go to https://console.developers.google.com/apis/credentials with this guide to generate a Client ID and Client Secret. Then, I followed this guide to learn how to get my Refresh Token.

Resources