How can I connect garb with a token from omniauth_google_oauth2? - google-analytics

I need to connect to Google Analytics. I'm using omniauth_google_oauth2 for authenticating the user with the app, and this gives me a token. The problem is that I need to connect to this user's Google Analytics account. To connect GA to my app I'm using the garb gem which has only two methods for authentication; username/password, and OAuth token. When I use the token provided by omniauth_google_oauth2, it doesn't work.
How do I create this new token using only the oauth_token that I get from the authentication with omniauth_google_oauth2?

I know I'm late to the party on this one but I solved a similar issue. You can't use omniauth_google_oauth2 with garb unless you use a fork of garb that supports oauth2. There is one here that is well maintained by Sija. However, you will need to use an oauth2 client object in order to create a session with this fork. You can get your user's profile set up using omniauth_google_oauth2 and make sure you save the refresh token for the user, then when you want to grab analytics data with garb, refresh the token with oauth2 and then pass that object into your garb session to pull the user's data. Here's an example after you have the refresh_token from omniauth stored somewhere:
client = OAuth2::Client.new YOURGOOGLEAPIKEY, YOURGOOGLEAPISECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => "/o/oauth2/auth",
:token_url => "/o/oauth2/token",
}
response = OAuth2::AccessToken.from_hash(client, :refresh_token => omniauth_refresh_token).refresh!
Garb::Session.access_token = response

I think the problem you're encountering is that garb will only authenticate a user using OAuth 1 (or a username/password combo), while omniauth_google_oauth2 is (obviously) OAuth 2.
The only solution I've found is to use Google's deprecated OAuth 1 implementation as follows...
Gemfile:
gem 'omniauth-google', :git => 'git://github.com/davidkpham/omniauth-google.git'
# This fork relaxes dependencies on omniauth itself
Initializer (for Google Analytics access):
provider :google, 'XXXXXXXXXXXX.apps.googleusercontent.com', 'YOUR_SECRET', scope: 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.google.com/analytics/feeds/'
On the callback, store some of the stuff passed back:
auth = request.env["omniauth.auth"]
session[:google_token] = auth.credentials.token
session[:google_secret] = auth.credentials.secret
Then construct an AccessToken for garb:
if session[:google_token] and session[:google_secret]
consumer = OAuth::Consumer.new('XXXXXXXXXXXX.apps.googleusercontent.com', 'YOUR_SECRET', {
:site => 'https://www.google.com',
:request_token_path => '/accounts/OAuthGetRequestToken',
:access_token_path => '/accounts/OAuthGetAccessToken',
:authorize_path => '/accounts/OAuthAuthorizeToken'
})
garbsession = Garb::Session.new
garbsession.access_token = OAuth::AccessToken.new(consumer, session[:google_token], session[:google_secret])
# Once we have an OAuth::AccessToken constructed, do fun stuff with it
ga_id = "UA-XXXXXXX-X"
profile = Garb::Management::Profile.all(garbsession).detect {|p| p.web_property_id == ga_id}
ga_monthly = GoogleAnalyticsDate.results(profile, :start_date => (Date.today - 30), :end_date => Date.today, :sort => :date)
puts ga_monthly
end

Related

How to always get latest Firebase Auth Token

Currently I am using this code to get the latest auth token for firebase, which I use in a header with Apollo or URQL to query something else to be validated...
async getToken(): Promise<any> {
return await new Promise((resolve: any, reject: any) => {
this.afa.onAuthStateChanged((user: any) => {
if (user) {
user.getIdToken(true).then((token: string) => {
resolve(token);
}, (e: any) => reject(e));
}
});
});
}
I am use getIdToken(true) to always make sure I get a valid token since the token expires after one hour and the custom claims could be updated at some point.
However, my code gets a new token every time, when really I only need to get a new token when the old one is expired, or there is new information in the token's custom claim.
Should I be using some for of onIdTokenChanged() ? Does firebase store all this automatically in the firebase localstoreage db (IndexedDB), or should I be using some form of localstorage and calculating the expiry time ?
Basically, what is the best way to minimize the number of refreshes to the token to speed up my app instead of getting a new token every time?
Thanks,
J
Unless you are using a custom solution with the REST API, the firebase client modules will automatically refresh the auth token with the refresh token when the old one expires.
As for updating the custom claims, you will have to communicate with the client app through some means such as a server response if you invoke a cloud function or a realtime database listener that the user is subscribed to if you are updating it based on 'external' conditions.

Custom provider claims in `additionalUserInfo.profile` are not available via firebase admin?

I'm following firebase/identity toolkit docs for a SAML identity provider. Upon successful login, the redirect result contains attributes derived from the provider:
provider = new firebase.auth.SAMLAuthProvider('saml.test-provider');
firebase.auth().signInWithRedirect(provider);
...
firebase.auth().getRedirectResult().then(function(result) {
if (result.credential) {
console.log(result.additionalUserInfo.profile) // Custom provider claims, e.g., {"provider-foo":"bar"}
}
}
From the docs, the same values are also available via
result.user.getIdTokenResult().idTokenResult.claims.firebase.sign_in_attributes
firebase.sign_in_attributes
These same attributes don't seem to be stored anywhere accessible in the firebase_admin SDK:
from firebase_admin import auth
user = auth.get_user(uid)
print(user.custom_claims) # => None ... *provider* claims aren't here
print(user.provider_data[0]) # => has `federatedId` and some others, but still no custom provider claims
Is there any way to get this info in the admin SDK? Any way to tell if it's even saved by Firebase? Do I need to capture it in firestore (and wouldn't that be risky since the user could fake claims coming from the provider?)
Thanks!
the additional SAML attributes are only persisted in the token claims accessible via:
result.user.getIdTokenResult().idTokenResult.claims.firebase.sign_in_attributes
They are not stored on the user record. Identity Platform/Firebase Auth does not persist additional user info in storage for privacy reasons.
However, you can always store the claims you need on the record via the Admin SDK.
You would send the ID token to your server, verify it, parse the claims you need and set them on the user record.
Here is sample via the Node.js Admin SDK.
app.post('/saveUserClaims', (req, res) => {
// Get the ID token passed.
const idToken = req.body.idToken;
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
const uid = decodedToken.uid;
// ...
const samlClaims = decodedToken.firebase.sign_in_attributes;
// You would filter the claims as there could be too many.
// You can also save these claims in your database, etc.
return admin.auth().setCustomUserClaims(uid, samlClaims)
.then(() => {
res.status(200).end();
});
}).catch(function(error) {
// Handle error
});
});
That said, in general there is no need to save these claims as they will always be available in the ID token and you can access them from your security rules or when you pass the ID token to your server for validation. This is a better way to do this as you don't run into synchronization issue where your DB is out of sync with the user's attributes.

Access Firebase Custom Token claims from the Web SDK

If I have a custom token with certain claims and I sign in to Firebase using it, is there any way to access those claims from inside app, using the Web SDK?
For example, if my custom token is like this
{
: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 => uid,
:claims => {:premium_account => is_premium_account}
}
I would like to know if there is something like (from inside the app):
firebase.auth.token.claims.premium_account
I'm not finding anything like this in the docs.
It looks like you want getIdTokenResult:
await firebase.auth().currentUser.getIdTokenResult()
claims is embedded in the token.
Here is an example code to extract the claims from the token using jwt-decode on a web client:
import jwt_decode from './jwt-decode';
firebase.auth().currentUser.getToken().then((token) => {
console.log(token);
console.log(jwt_decode(token));
});
Here is the documentation on it: https://firebase.google.com/docs/auth/admin/custom-claims
I think the gist of it, is once you have custom claims appended to a user via backend code (admin sdk OR firebase functions), you can base64 decode the currentUser token.
The documentation references a mozilla article on javascript base64 decoding: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
You can JSON.parse the decoded token and your custom claims will show up there. The documentation is pretty good about it.

Where to find auth.token data, inside firebase objects

I am using signInWithCustomToken, after authentication I can not find where is stored my custom claims data which I have set in the server side(createCustomToken).
I can see them in firebase rules via auth.token, but how can I access them through firebase objects from within my javascript code.
The information in the token is not automatically available to your application code. But it is embedded in the token, so you can decode it yourself:
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace('-', '+').replace('_', '/');
return JSON.parse(window.atob(base64));
};
var user = firebase.auth().currentUser
user.getToken().then(data => {
console.log(parseJwt(data));
});
The function to parse the JWT comes from this question: How to decode jwt token in javascript
You'll note that it doesn't verify that the ID token is valid. That seems fine to me in client-side code, since the information will be used by the user themselves anyway. But if you do want to verify the token, you'll have to use a more involved method.

Symfony FOSOAuthServerBundle get tokens programmatically?

Using the standard endpoint for FOSOAuthServerBundle (with FOSUserBundle), I can retrieve an access and refresh token by providing a client_id, client_secret, user and password combination. The response looks like this:
{
"accessToken": "YTg2ZTJkNTY2MGM5MGQyNzZjYjkyZWMwYzg1YTZmZTZmOTIyMzAxNDY2MTkwZDU5ODYzZTAzYmIyNDI0YTQ4ZQ",
"expiresIn": 3600,
"tokenType": "bearer",
"refreshToken": "OTU1MGZhNDQ2ODFkZDUzMmQ4Y2FhNTk5OWM0NWFlNDk0YTY0ZDZhOTRjZTUwM2JlYTE3MDkxYzU3ZWY1OGRkYQ"
}
My question is, how can I retrieve similar data programmatically by passing in the client and user credentials? I.e. How can I make the same call from another part of my application without going via HTTP (slow), but rather directly via the bundle code (fast)?
I'm sure there must be an easy way of doing this, but the best I can find so far is this https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/issues/347 which doesn't really achieve the same thing as the HTTP call.
Here is how you can get the same response directly from the fos_oauth_server.server service using a request object:
$grantRequest = new Request(array(
'client_id' => $clientId,
'client_secret' => $clientSecret,
'grant_type' => 'password',
'username' => $username,
'password' => $password
));
$tokenResponse = $this->get('fos_oauth_server.server')->grantAccessToken($grantRequest);
$token = $tokenResponse->getContent();
My understanding is that you're using password grant type. This would require that your application knows a user and password pair to get a token. I would suggest instead to use client_credentials grant type.
Using the FOSOAuthServerBundle you should be able to get an access token with something like (in a ContainerAware context)
$this->get('fos_oauth_server.server')->grantAccessToken($request)
Here as you can see a Request object is required, but you can forge this object easily
In alternative you could try
$this->get('fos_oauth_server.server')->createAccessToken($client, null)
Where $client is an instance of you OAuth client.

Resources