I'm working on an objective-c iOS app. I want to use auth0 for authentication ( https://auth0.com/ ) and I want to use Firebase for the database backend.
I've gone through all the auth0 documentation and I've got authentication working for:
Facebook, Google+, twitter, self registration.
The problem:
The documentation kinda falls off at the point where I need to integrate the authentication model with Firebase, it gives me this one page and I'm not really sure what to now. Has anyone does this integration before and can you lead me down this path? I'm kinda new at this.
BlockquoteConfiguring Token content
As with any other API registered in the dahsboard, Auth0 will issue a Firebase token through the Delegation endpoint. This allows you to exchange a token for another one.
The contents of the Firebase token are generated by convention, copying all properties contained under the firebase_data attribute in the input token used in the Delegation call.
You can generate these very easily with a rule:
user.firebase_data = {
user_id: new Buffer(user.email).toString('base64'),
company: !user.isSocial ? context.connection.replace(/\./g, '-') : null,
foo: 'bar'
};
In the example above, the two properties user_id and company will be generated after calling the delegation endopint, and both will be made available to Firebase.
Blockquote
I have done this for Javascript in the browser, not ios/Objective C. But in concept, you need to do four things:
Setup
Configure your Auth0 account to allow Firebase delegation, and provide your Firebase token. This part is covered by Auth0's ios/objective C docs for Firebase, on the Firebase tab.
(optional) Create an Auth0 rule to set properties on delegated Firebase tokens. You have this in your snippet above.
Auth0 Rule for setting Firebase Token properties:
user.firebase_data = {
user_id: new Buffer(user.email).toString('base64'),
company: !user.isSocial ? context.connection.replace(/\./g, '-') : null,
foo: 'bar'
};
The properties you set here will be available in Firebase security rules.
Authentication Flow
Auth0 has a swift sample that seemed likely to be helpful to you. You need to do two things:
After the user authenticates successfully, make a second Auth0 request for a delegated Firebase access token from Auth0, see sample line 65.
Use the new delegated token with a Firebase object via its authWithCustomToken method, see sample line 73.
Related
Context:
I'm making an app that allows users to sign in with Google and which then makes calls to the Google Ads API on their behalf.
What works:
Users can sign in and grant the app the necessary permissions, and the API calls go through successfully.
What I'm struggling with:
I'd like to separate the login flow from the permissions flow. In other words, I would like users to be able to log in without granting the app any extraneous permissions, and then, later on, decide if they want to connect to their Google Ads account. This is when Google would prompt them for the necessary permissions.
Relevant code:
I'm using next-auth to handle authentication, setting up the Google provider like this:
./src/pages/api/[...nextauth].ts:
[…]
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
scope: 'https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/adwords'
}
}
}),
[…]
You can also look at my reproduction repo, a skeletal app that does nothing but make an API call to Google Ads on behalf of the logged-in user. Bootstrapped with create-t3-app using Next.js, tRPC, Tailwind and Prisma.
UPDATE: I can wrap NextAuth(options) and take control of the endpoints
I've discovered that I can use custom initialization of the NextAuth API routes, which should provide me with an opportunity to alter the scope. Unfortunately, I'm still not sure how to provide an askForGoogleAdsPermissions flag to next-auth/react's signIn function in such a way that my customized API handler will be able to tell the difference and add or omit the authorization key in the Google Provider options accordingly.
Update #2: I can provide extra parameters to signIn
Turns out the signIn function can accept additional parameters as a third argument.
This allows me to separate the login from the permissions, but now the API calls I'm making to Google Ads are failing with "PERMISSION_DENIED: Request had insufficient authentication scopes.", despite having successfully granted permissions.
I can confirm the permissions are granted in my Google account, but when I look at the Prisma database managed by next-auth, I don't see the new scope in my account.
Perhaps I can use the signIn callback to make sure the scope is updated? My problem there is that the account I get as a parameter to my signIn callback has the old scope…
To separate the login and grant flows, I had to:
Include only the login scope in the NextAuth GoogleProvider.
Call the signIn method without arguments for a regular sign in
Call if with extra parameters to override the default scope when connecting to Google Ads: signIn('google', undefined, { scope: googleAdsScope })
Save the new scope to the database in the signIn callback
Make sure this isn't overwritten by a token refresh
See my repro repo for the full, working code.
I’m updating a third party app that currently integrates with Twinfield using the session’s method with username and password to use the oAuth method.
In confused by the documentation though... do I still need to use the sessions or when using oAuth do I just call the endpoint(s) by passing the access token in the header as normal?
Also their Soap definition has four properties, the usual ClientID and Secret but also accessSecret? What’s that?
TLDR: you no longer need to use the sessions and SelectCompany; when you have the access token you can use that and the company code directly in the header.
You can obtain the access token as described here.
The documentation is a bit unclear on how to use the access token in your calls.
In the old username/password/session flow, you referred to a SessionID in the SOAP Header, and you would do a SelectCompany call to select the relevant target ("administratie").
In the OAuth flow, the SessionID is no longer relevant. Once you obtained a valid access token, you should set that in the header using the AccessToken field.
Instead of the old SelectCompany call, you can set the CompanyCode directly in the header. So if you have obtained an access token eyWhatANiceToken, and want to retrieve data for company "My Company BV [130001]" you have set AccessToken to eyWhatANiceToken and CompanyCode to 130001 in the header.
You can request the available codes using the list offices call
I have implemented a AWS Lambda function and used the gateway to return the fulling data:
var param =
{
IdentityPoolId: "actualIdentityPoolId",
Logins: {} // To have provider name in a variable
};
param.Logins["com.testing.userLogin"] = userId;
cognitoidentity.getOpenIdTokenForDeveloperIdentity(param,
function(err, data)
{
if (err) return fn(err); // an error occurred
else fn(null, data.IdentityId, data.Token); // successful response
});
So the identityId and token get sent back to the ios device. In my device I try to connect to an AWS DynamoDB table but access is denied. How do I use the identityId and token to gain access to the tables?
I have set up roles in IAM for Unauth which denies Dydnamo and Auth which gives access to the tables through its policies.
I am trying to implement authentication using: http://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
I see there are two flows which are Basic and Enhanced. The documentation says most users will use the enhanced flow and that implements GetCredentialForIdentity.
How is that implemented in my ios code so that I can switch my role from unauth to auth and can access to dynamodb? How long will this access last? I would like to do this all in my ios code instead of using lambda or something else like that.
If your user is unauthenticated, then logs in you need to clear your credentials, and your 'logins' method should now return a properly updated logins map.
Here is the documentation to help you:
http://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html
Double check your DynanoDB Roles for authenticated access your DynamoDB resource. An example role for this are on the following page of the developer guide you referenced. The page is called "IAM Roles" and the last section is the important one: "Fine-Grained Access to Amazon DynamoDB".
Stick with your plan to use the Enhanced Authflow. It is recommended and makes less calls to authenticate (your users will appreciate this). Just make sure you mobile clients call GetCredentialsForIdentity from iOS.
From the Enhanced Authflow documentation further down your page:
The GetCredentialsForIdentity API can be called after you establish an identity ID. This API is functionally equivalent to calling GetOpenIdToken followed by AssumeRoleWithWebIdentity.
The AssumeRoleWithWebIdentity is the important piece that allows your user to assume the Role that gets access to the DynamoDB resource. Cognito will take care of the rest as long as you set up the Roles correctly within the Cognito console:
In order for Amazon Cognito to call AssumeRoleWithWebIdentity on your behalf, your identity pool must have IAM roles associated with it. You can do this via the Amazon Cognito Console or manually via the SetIdentityPoolRoles operation (see the API reference)
I have integrated my User Pools setup with the federated identity flow with the 9 steps from the relevant documentation. I'm following the documentation and using the enhanced auth flow.
There is however an additional step which I cannot quite understand that is possibly causing my experienced problem with the IOS SDK. The guide mentions that:
After the user is authenticated, add that user's identity token to the
logins map in the credentials provider. The provider name will depend
on your Amazon Cognito Identity user pool ID. It will have the
following structure: cognito-idp..amazonaws.com/
Then it offers the following IOS SDK snippet:
AWSCognitoIdentityUserPool *pool = [[AWSCognitoIdentityUserPoolalloc] initWithClientId:#"YOUR_CLIENT_ID"clientSecret:#"YOUR_CLIENT_SECRET"poolId:#"YOUR_USER_POOL_ID"];
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvideralloc] initWithRegionType:AWSRegionUSEast1identityPoolId:#"IDENTITY_POOL_ID"identityProviderManager:pool];
But from my understanding, there isn't any logins map added in the credentials provider in this piece of code. If you would look at the JavaScript and Java version you will see that this is set explicitly. After some digging around I also found a method for the IOS-SDK, namely a setLogins method, however this method is deprecated. Based on the documentation it would make sense that I could initiated it as follows:
[self.credentialsProvider setLogins:#{#"cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>": #"id_token"}];
However, this results in a depreciation warning.:
setLogins is deprecated. Use 'AWSIdentityProviderManager' to provide a valid logins dictionary to the credentials provider
The AWSIdentityProviderManager protocol does seem to offer a logins method, however this is not documented.
So my question is: how could I add my logins map in my credentials provider?
You do not add the logins dictionary to the credentials provider. AWSIdentityProviderManager defines the following method:
- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins;
It asynchronously supplies the logins dictionary to the credentials provider, and AWSCognitoIdentityUserPool conforms to AWSIdentityProviderManager. So, the code snippet is all you need. If you are experiencing an issue, it is not related to the logins dictionary.
I'm making requests against the Instagram API from a mobile app. Currently, I'm just directing the user to the Instagram auth url and specifying the response type to be "access_token". Specifying this response_type is known as implicit auth.
Explicit auth: response_type=code
Implicit auth: response_type=access_token
I'm trying to get around needing to stand up a web service to facilitate explicit auth. This would be necessary because in explicit auth flow, the Instagram API needs to make a call to a redirect URL and pass in a "code" parameter. The code would then be used by my server-side code to make a final request to Instagram for an access token.
It's much more efficient for a mobile app to use implicit flow because no extra privately-maintained auth service needs to be stood up to handle it.
Instagram supports the following scopes:
basic - to read any and all data related to a user (e.g.
following/followed-by lists, photos, etc.) (granted by default)
comments - to create or delete comments on a user’s behalf
relationships - to follow and unfollow users on a user’s behalf
likes - to like and unlike items on a user’s behalf
When I make any other type of scope specification besides "basic", I get the following response when the user provides the credentials at the auth URL:
{"code": 400, "error_type": "OAuthException", "error_message": "Invalid scope field(s): basic+likes"}
Any combination of scopes other than "basic" gives the same response.
So, my question are these:
Is explicit auth required in order to specify scopes beyond "basic"??
Do I need to specify response_type=code in order for extended scopes to work?
Is this an Instagram limitation, or is it a limitation of OAuth 2.0?
Thanks in advance.
I just tried with implicit oauth flow with my client_id and scope=basic+likes and it worked. Replace the url below with your client_id and redirect_uri, and try.
https://instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=REDIRECT-URI&response_type=token&scope=basic+likes
May be Instagram is not allowing scope other than basic with new client accounts...
The answer here is that YES, scopes can be requested by implicit auth flow just fine. My problem was related to an OAuth component that I was using. The component was silently URL-encoding the value of the scope param, which was rejected by the Instagram auth endpoint. I updated the component (Xamarin.Auth) to accomodate a non-encoded scope param and issued a pull request.
Thanks to #krisak for providing a working URL that I could test.
So I had similar issues regarding the encoding of the + when trying to get permission for multiple scopes (basic, likes, comments). The solution I found was to use spaces between the individual scopes:
In the config/initializers/omniauth.rb file:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :instagram, 'TOKEN', 'SECRETKEY' , {:scope => "basic likes comments"}
end
Unfortunately starting from April 14th 2015 new clients cannot get access for any scope but basic. Official message could be found at the client configuration page:
Starting April 14th 2015, new clients need to request access to be able to post likes, follows, and comments. For more information please read the Developer Blog at http://developers.instagram.com.
The message refers following blog entry: http://developers.instagram.com/post/116410697261/publishing-guidelines-and-signed-requests
Instagram requires personal request to be sent to enable scopes for your application (client ID), but your app has to meet certain conditions described in the blog entry.
i have the same problem i found this solution and works fine
Go to Manage clients under instagram/developer. Then click edit under your app and uncheck Disable Implicit OAuth. It will now work as intended.
Instragram changed this for a reason though, so should probably think twice before going public with your app: http://instagram.com/developer/restrict-api-requests/
At this time, May 2015, YES.
As explained on instagram documentation about authentication:
The Instagram API uses the OAuth 2.0 protocol for simple, but
effective authentication and authorization. OAuth 2.0 is much easier
to use than previous schemes and developers can start using the
Instagram API almost immediately. The one thing to keep in mind is
that all requests to the API must be made over SSL (https:// not
http://).
You first need to register your app here and then, with CLIENT ID provided by instagram, you can do this request:
https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code
Where you have to put your client_id and redirect_uri.
Just for information, in redirect_uri field you can insert also
http://localhost
you must be add "+" between scopes like that is "basic+comments+follower_list+likes+public_content+relationships"