Google OAuth2 fails to redirect back to site, instead takes me to Google's My Account Page - google-oauth

I am using Google's PHP API client within a Laravel application to authorize a user via OAuth2 for the Analytics read-only API. I've used similar code on a previous project without issues, but this time it takes me to either Google's My Account page, or YouTube's account page, depending on the account I try to authorize with.
I have verified it is receiving the correct redirect_uri.
Here's some relevant code:
$google = new \Google_Client();
$google->setAuthConfig(resource_path('../client_secrets.json'));
$google->setRedirectUri(url()->to('/google/authorize'));
$google->addScope(\Google_Service_Analytics::ANALYTICS_READONLY);
$google->setAccessType("offline");
$google->setApprovalPrompt('force');
// No code? Then we need to authorize with Google.
if (! $request->has('code')) {
$authUrl = $google->createAuthUrl();
return redirect()->to($authUrl);
}

FIXED.
Turns out Google was redirecting properly but for some reason the code wasn't recognizing it had the code query value attached properly. Split the redirect to a different method and got it working that way.

Related

Keycloak - Multi/2FA Factor - OTP - QR Code - Custom Login Screen - Rest API

I have my own Login page where user enters username/password.
This username/password are used to login through Keycloak Rest API.
http://localhost:8080/auth/realms/Demo/protocol/openid-connect/token
input - {username,password,grant_type,client_secret,client_id}
And in response i get access token.
Now i wish to enable Authenticator (Google Authenticator). I have enabled it from backend. Now if user wishes to login thorugh my application, my login page i need to get below details.
1.) Somehow i need to include QR Code that appears on keycloak login page post username/password validation to show on my login screen for the first time login once user enter username/password. So do we have any API which return Keycloak QR code image in response.
2.) Subsequent login i will have OTP field, so need a REST api to pass OTP along with username/password.
Please help with REST API if keycloak has any. Integrating through Javascript.
Similar flow as described in use case 1 here
Just want to use keycloak as a database, doing all operation for me, input will be my screen. I do want redirection of URL's while login in and yet should be standalone deployed.
I've managed to implement this through the rest API of Keycloak. To realize this, you need to extend Keycloak yourself with a SPI. To do this create your own Java project and extend org.keycloak.services.resource.RealmResourceProvider and org.keycloak.services.resource.RealmResourceProviderFactory. You can find more information in the official docs (https://www.keycloak.org/docs/latest/server_development/#_extensions), github examples and other stack overflow posts how to do this.
Once you got this up and running you can implement it like this:
#GET
#Path("your-end-point-to-fetch-the-qr")
#Produces({MediaType.APPLICATION_JSON})
public YourDtoWithSecretAndQr get2FASetup(#PathParam("username") final String username) {
final RealmModel realm = this.session.getContext().getRealm();
final UserModel user = this.session.users().getUserByUsername(username, realm);
final String totpSecret = HmacOTP.generateSecret(20);
final String totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
return new YourDtoWithSecretAndQr(totpSecret, totpSecretQrCode);
}
#POST
#Path("your-end-point-to-setup-2fa")
#Consumes("application/json")
public void setup2FA(#PathParam("username") final String username, final YourDtoWithData dto) {
final RealmModel realm = this.session.getContext().getRealm();
final UserModel user = this.session.users().getUserByUsername(username, realm);
final OTPCredentialModel otpCredentialModel = OTPCredentialModel.createFromPolicy(realm, dto.getSecret(), dto.getDeviceName());
CredentialHelper.createOTPCredential(this.session, realm, user, dto.getInitialCode(), otpCredentialModel);
}
The secret received with the GET must be send back with the POST. The initial code is the one from your 2FA app (e.g. Google Authenticator). The QR code is a string which can be displayed in an img with src 'data:image/png;base64,' + qrCodeString;
I know this is an old question, but I've recently been looking at something similar, and so thought it would be potentially valuable to share what I have found for others who may be looking into this and wondered what the possibilities are.
TL;DR
You can only really use the existing Keycloak actions to do this or embed the user account management page found at https://{keycloak server URL}/auth/realms/{realm name}/account in an iframe. That's it, I'm afraid. In my opinion it is currently best to just assign actions directly to accounts or use the Credential Reset emails to assign actions; both of these can be done via the Admin API if desired:
Send Credential Reset email containing assigned actions:
https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_executeactionsemail
Set actions directly on the account (include the actions in the requiredActions portion of the user JSON that you send in the body to the endpoint):
https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_updateuser
Background is that as part of a project that I have been working on we wanted to see if we could have an integrated way for users to set up their initial password and OTP device when a new account has been created for them, since the default method of sending them an email from Keycloak using the "Credential Reset" functionality has the limitations that a) it doesn't provide a link to the application itself unless you override the theme, and if you have multiple instances of the application for different users you have no way of knowing which instance to provide the link for, so may have to end up including a list of them, and b) it often doesn't feel truly native to the application, even with changes to the theme. If you're sensible though, I'd suggest you stop and just use this functionality - please see the TL;DR section above for details.
So, in short there is NO API endpoint for receiving a QR code to set up an OTP device. There are two places, however, where the QR code can be retrieved from - the OTP device setup screen when you log in as a user who has had a "Configure OTP" action assigned to their account, and the user's own account management screen.
The first option of the Configure OTP action screen is a non-starter. It only shows up when you log in, and so by definition the user has to log in to Keycloak via the Keycloak login page in order to trigger the page to be displayed. At this point you're already on a Keycloak page instead of one of your app's pages, and so unless you can get very creative with changes to these Keycloak pages via a custom theme, tapping into this page isn't really an option.
The second option is more interesting, but far from ideal. Every user who has logged in has access to an account management page that can be found at https://{keycloak server URL}/auth/realms/{realm name}/account. This page allows you to do things like change your name, password, etc. and it also allows you to add an OTP device if you don't already have one, or delete any existing OTP devices associated with your account. This OTP device tab of the account management page can be reached directly via https://{keycloak server URL}/auth/realms/{realm name}/account/totp.
As I mentioned, there isn't an API that you can access to view the QR code that shows up on this page. The only way it is accessible is via the GET request to https://{keycloak server URL}/auth/realms/{realm name}/account/totp, which returns the HTML for the page I've already mentioned. Okay great, so can we scrape the QR code programmatically and then put it in our own page on our application? Err, no, not really. You see, whilst a lot of the Keycloak API endpoints rightly allow you to send a bearer token (e.g. access token) in the authorization header to access and endpoint, this page will not accept a bearer token as a means of authentication/authorization. Instead it uses a session cookie that is locked down to the Keycloak URL. This cookie is set when you log in to your application via the Keycloak login page, and so is available to this account management page when you navigate to it, having already logged in, and since the account management page uses the same server and domain name as the original Keycloak login page, it has access to the cookie and can let you in. This cookie cannot be sent by your application to e.g. your own REST API to then programmatically call the account management page and scrape the QR code, because your application doesn't have access to it for good security reasons. This might be something you can change in Keycloak somewhere, but if there is I would strongly recommend against changing it.
So if we can't scrape the page from our own server, can we do something on the front-end? Well, as mentioned, your application doesn't have access to the session cookie but if you make a request (e.g. using fetch or axios) in your front-end JavaScript to the account management page then that request will send the cookie along with it, so that could work right? Umm, well actually you will get hit with an error message in this scenario due to CORS. CORS is Cross-Origin-Resource-Sharing and in order to allow the Keycloak page to be accessed then you would have to open up the settings on the server to allow it to be accessed from your website's address. I've seen some articles that look at how you can open up your CORS settings on Keycloak if you wish but I'd be very nervous about doing this. I don't know enough about the internals of Keycloak and how it operates to comment on how much of a security risk this is, but I certainly wouldn't recommend it. There some information here (Keycloak angular No 'Access-Control-Allow-Origin' header is present) on changing the "Web Origins" setting of your application's Keycloak client, but this opens up your application to some serious potential abuse. There is also the MAJOR issue that even if you scraped the QR code, the device isn't actually added to the user's account (even though it appears in the authenticator app) until you enter a code into the page that the QR code is on and click Save. Since there isn't an API endpoint that you can use to complete this operation, I therefore don't think that this option is viable either. I've tried out whether or not you can use the token retrieval endpoint at https://{keycloak server URL}/auth/realms/{realm name}/protocol/openid-connect/token to see if making a request with your username/password/otp code will somehow "register" your device and complete the process, but although you can get a token this way, and it doesn't complain about the otp code, it doesn't actually take any notice of the code because as far as it's concerned the user's account doesn't have a device registered with it. So we have to use the form on the account management page in order to complete this registration process.
So the final way of possibly doing this is.... an iframe. Sorry, yeah it's rubbish but that's all your left with. You can have the iframe point at your account management page, and because the user is logged in then they will be able to see the contents from your application's page. You can use relative positioning, fixed width and height and remove scroll bars to ensure that you ONLY show the QR code and the fields for the one time code, device name, and the Save/Cancel buttons. This, sadly, seems to be the only option at the moment, and due to how nasty and unreliable iframes can be in general - they certainly don't feel native to your application, and you'll need to override your Keycloak theme to get the page in question to look more like your app - I'd recommend steering clear of this and using the standard approach of using Keycloak actions and the Admin API instead.
If you've made it this far, congratulations, you win at Stack Overflow :-)

How do I retrieve the API data from the LinkedIn Strategy for Opauth?

I installed the Google Opauth Strategy and it's working brilliantly. The LinkedIn one acts a little differently; in the directions for this strategy it specifies that no OAuth Redirect URL is needed (on the LinkedIn Developer website). I tried leaving this out, and kept getting this error:
'Invalid redirect_uri. This value must match a URL registered with the API Key.'
This error is also on the demo for the plugin here: http://opauth.org/#demo
So I changed it up a bit and placed my redirect URL there just in case, like my Google one has, and it seemed to have worked! It took me to the LinkedIn login screen, I put in my credentials, and it redirected me back to my application. The trouble is, no data was returned this time.
My defaults in LinkedInStrategy.php looks like this:
public $defaults = array(
'redirect_uri' => 'http://example.com/users/login',
'response_type' => 'code',
'scope' => 'r_fullprofile r_emailaddress r_contactinfo'
);
In my controller I have this:
$_SESSION['log'] = $this->data;
And in my view I have this:
debug($_SESSION['log']);
This is just to see if the data is setting. When I log in with GoogleStrategy.php settings, this line displays all of the data and I use it to log me in. Perfect. But with the LinkedIn strategy, I just get an empty array. Anybody have any ideas?
Edit:
According to the LinkedIn API documentation here, my code returned is the correct code returned upon successful authentication (redirect uri, code, and state in the response URL). So I know everything's correct thus far to connect, just obtaining the data must be different than the $this->data return method stated in the regular Opauth documentation.
I solved it! I changed the redirect_uri back to default:
'redirect_uri' => '{complete_url_to_strategy}oauth2callback',
In the LinkedIn Developer API area, I placed this URL in the OAuth 2.0 Redirect URLs:
http://example.com/auth/linkedin/oauth2callback
So the OAuth Redirect URL DOES need to be set, unlike their instructions here.
2. Create LinkedIn application at https://www.linkedin.com/secure/developer
Enter your domain at JavaScript API Domain
There is no need to enter OAuth Redirect URL
Furthermore, it specifically has to be http://www.example.com/auth/linkedin/oauth2callback. If it is not that URL exactly, it will not work, unless you change some of the config files yourself and the name of functions. My error was that I set a custom page for it to go to, rather than going through its oath2callback function THEN heading to the redirect page.

When is access_type = Online appropriate? :OAuth2 - Google API

When requesting OAuth credentials, I can specify the access_type to be Offline or Online.
Opting for the Online access type forces the users to approve access to my app each time they login. Why is that? Hasn't the user already approved my app?
Update #1:
I have my approval_prompt set to 'auto'.
If I just log out of Google without deleting any cookies, it doesn't prompt me again. But deleting the cookies brings back the grant screen.
Update #2:
It works fine through the OAuth Playground. http://code.google.com/oauthplayground/
Using OAuth 2.0 for Web Server Applications
https://developers.google.com/accounts/docs/OAuth2WebServer
Update #3:
Relevant code snippets
Helper method to generate OAuth URL
def build_auth_uri
return #client.authorization.authorization_uri(
:access_type => :online,
:approval_prompt => :auto
).to_s
end
Calling the Helper method in the View
Connect Me!
Generated OAuth URL on the webpage
https://accounts.google.com/o/oauth2/auth?access_type=online&approval_prompt=auto&redirect_uri=http://localhost:3000/gclient/gcallback&response_type=code
There is one other parameter that comes into play in these flows and I suspect you're running into it. It's the approval_prompt parameter.
When access_type=online you are also allowed to specify a value for approval_prompt. If it is set to approval_prompt=force, your user will always be prompted, even if they have already granted.
On the other hand, when access_type=offline, approval_prompt can only be set to approval_prompt=force, but to make up for this restriction you're also provided a refresh_token which you can use to refresh your access token.
Check the URL that your access_type=online is opening. Try setting approval_prompt=auto. The grant screen should only appear the first time.

LinkedIn OAuth Request with no Verifier

Introduction:
Hi everyone, i am currently writting a lib to make generic OAuth Requests in C#.
The first part, of getting a Token and a Token secret is working for Twitter and Linkedin already ( RequestTokens ), but as soon as i jump to the Authorization part it does not work for Linkedin,but it does for twitter.
Useful Informations
I am currently using a method to generate AUTHORIZATION_URL for the Request. For Example :
https://api.linkedin.com/uas/oauth/authorize?MyToken
Same for Twitter,using its own url. Both services are getting a correctly generated URL, and the window that pops up when i copy it in the browser is correcly.
After the user hits the button, different things happens depending on the service.
1 - Twitter : The browser redirects me to the Callback Page, and on the url there is a Verifier that i am currently parsing and storing it. Perfect.
2 - LinkedIn : In the sample i have,everything works just as twitter does, but when i use my own lib, there is no redirect for the callback url,instead, i am getting redirected to a url with a oob? tag, and a verifier number is shown in the screen, instead of appearing in the querystring.
Doubt:
What should i do to make sure that the OAuth method i will be using is the Normal one,instead of the Out Of Band method. I am making sure that the CALLBACK URL that i set in the lib, is being used for the Signature on the First Request (REQUEST_TOKEN STEP). Also, twitter works when i do this.
Any idea of whats happening ? Let me know if there is any useful information i can add to make sure that my question will be as complete as possible.
Thanks in advance
I solved it.
I forgot to add the Callback parameter to the Signature in the BaseGenerator,instead, it was commented.
Thanks anyways for everyone

Authorizing for Google ToDo List (AuthToken, secid)

I'm trying to get access to the Google's todo feed with this url:
https://www.google.com/calendar/tdl?secid=<SECID>&tdl={%22action_list%22%3A[{%22action_type%22%3A%22get_all%22%2C%22action_id%22%3A%221%22%2C%22list_id%22%3A%2215052708471047222911%3A0%3A0%22%2C%22get_deleted%22%3Afalse}]%2C%22client_version%22%3A-1}
If I open this in my browser with a correct secid, it shows me right what I want.
Now, the question is: how do I get secid programmatically (specifically, in a java program)? I have access to the authToken (from CalendarService), but I have no clue how to use it to authorize my access to the URL above.
I tried to use the url http://google.com/accounts/ServiceLogin, but I didn't find any examples.
Any help, please?
From what I read secid is a session ID obtained from browser's cookies. Whereas your case uses Java which implies a server app. If that is the case, you want to drop the idea of using secid entirely.
Instead, you want to check out Google's OAuth2 documentation. If you are using Java, most likely you would be interested in the web-server OAuth flow. Pay special attention to the sequence diagrams.
The key steps include:
1) Obtain an authorization code from Google OAuth with the user's consent. For that, you redirect the user to Google with the appropriate scope. Check the list of calendar scopes for your case. Once the user consents, Google redirects back to you with an authorization code.
2) Call Google OAuth with the authorization code and your app's credentials to exchange for an access token.
3) Call Google's Calendar API using the access token.
And if you use Google's Java client as suggested by #ChaosPredictor, chances are some of the steps are already wrapped into the Java client (and your code will be much simpler).