yammer oauth against external network? - oauth-2.0

With yammer OAuth, is it possible to register an app on an external yammer network and then authenticate users against this external network, rather than the primary network?
update 9/30/2014 -- to clarify, my goal is to use yammer oauth as a gateway to my own (python based) application, allowing only users of the external network in.
i've got this working on a non-SSO primary & external network combo. after the external network user successfully signs in via yammer, and they are redirected to my callback url, i make a call to /networks/current. i can then check to see if the target external network id appears in their list, and if so let them in.
when i switch my application to use API credentials for an app registered on an SSO enabled primary network & non-SSO external network, and try to login with an external network user, things stop working. after the yammer sign in process completed (note: yammer kicks them out to the SSO endpoint and sign in is completed there), and the user is redirected back to my app, i get a 403 forbidden error. the process never makes it to my call to get their networks...if i try logging in with a user of the primary (SSO enabled network), it works (my custom login fails them since they are not a member of the correct network).

You need to globalize your SSO App to get things work. For this you need to raise a request to the Yammer helpdesk.

I tried this same implementation recently and didn't work because the userID (of the authenticated user) that's associated with the external network's access token is the home network's UserID. I worked around it by:
Opening Chrome
Sign into the external network
Right click -> Inspect Element
Click Network Tab
Refresh page
Doubleclick on any row containing ‘…./api/v1/…’
Click headers tab in side panel
Token is found in “Authorization: Bearer …”
You can then pass that bearer token in your REST request header
NOTE: Mine was an SSO enabled network so your mileage may differ :)

Related

How does keycloak determine which signature algorithm to use?

I'm writing an application that uses keycloak as its user authentication service. I have normal users, who log in to keycloak from the frontend (web browsers), and service users, who log in from the backend (PHP on IIS). However, when I log in from the backend, keycloak uses HS256 as its signature algorithm for the access token, and thus rejects it for further communication because RS256 is set in the realm and client settings. To get around this issue, I would like to "pretend to be the frontend" to get RS256 signed access tokens for my service users.
For security reasons, I cannot give the HS256 key to the application server, as it's symmetrical and too many people can access the server's code.
I am currently debugging the issue using the same user/pw/client id/grant type both on the frontend and the backend, so that cannot be the issue.
So far I have tried these with no luck:
copying the user agent
copying every single HTTP header (Host, Accept, Content-Type, User-Agent, Accept-Encoding, Connection, even Content-Length is the same as the form data is the same)
double checking if the keycloak login is successful or not - it is, it's just that it uses the wrong signature algorithm
So how does keycloak determine which algorithm to sign tokens with? If it's different from version to version, where should I look in keycloak's code for the answer?
EDIT: clarification of the flow of login and reasons why backend handles it.
If a user logs in, this is what happens:
client --[login data]--> keycloak server
keycloak server --[access and refresh token with direct token granting]--> client
client --[access token]--> app server
(app server validates access token)
app server --[data]--> client
But in some occasions the fifth step's data is the list of users that exist in my realm. The problem with this is that keycloak requires one to have the view-users role to list users, which only exists in the master realm, so I cannot use the logged in user's token to retrieve it.
For this case, I created a special service user in the master realm that has the view-users role, and gets the data like this:
client --[asks for list of users]--> app server
app server --[login data of service user]--> keycloak server
keycloak server --[access token with direct granting]-->app server
app server --[access token]--> keycloak server's get user list API endpoint
(app server filters detailed user data to just a list of usernames)
app server --[list of users]--> client
This makes the the list of usernames effectively public, but all other data remains hidden from the clients - and for security/privacy reasons, I want to keep it this way, so I can't just put the service user's login data in a JS variable on the frontend.
In the latter list, step 4 is the one that fails, as step 3 returns a HS256 signed access token. In the former list, step 2 correctly returns an RS256 signed access token.
Thank you for the clarification. If I may, I will answer your question maybe differently than expected. While you focus on the token signature algorithm, I think there are either mistakes within your OAuth2 flows regarding their usage, or you are facing some misunderstanding.
The fact that both the backend and frontend use "Direct Access Granting" which refers to the OAuth2 flow Resources Owner Credentials Grant is either a false claim or is a mistake in your architecture.
As stated by Keycloak's own documentation (but also slightly differently in official OAuth.2 references):
Resource Owner Password Credentials Grant (Direct Access Grants) ... is used by REST clients that want to obtain a token on behalf of a
user. It is one HTTP POST request that contains the credentials of the
user as well as the id of the client and the client’s secret (if it is
a confidential client). The user’s credentials are sent within form
parameters. The HTTP response contains identity, access, and refresh
tokens.
As far as I can see the application(s) and use case(s) you've described do NOT need this flow.
My proposal
Instead what I'd have seen in your case for flow (1) is Authorization Code flow ...
assuming that "Client" refers to normal users in Browser (redirected to Keycloak auth. from your front app)
and assuming you do not actually need the id and access tokens back in your client, unless you have a valid reasonable reason. As the flows allowing that are considered legacy/deprecated and no more recommended. In this case, we would be speaking of Implicit Flow (and Password Grant flow is also discouraged now).
So I think that the presented exchange (first sequence with points 1 to 5 in your post) is invalid at some point.
For the second flow (backend -> list users), I'd propose two modifications:
Allow users to poll the front end application for the list of users and in turn the front-end will ask the backend to return it. The backend having a service account to a client with view-roles will be able to get the required data:
Client (logged) --> Request list.users to FRONTEND app --> Get list.users from BACKEND app
(<--> Keycloak Server)
<----------------------------------------- Return data.
Use Client Credentials Grant (flow) for Backend <> Keycloak exchanges for this use case. The app will have a service account to which you can assign specific scopes+roles. It will not work on-behalf of any user (even though you could retrieve the original requester another way!) but will do its work in a perfectly safe manner and kept simple. You can even define a specific Client for these exchanges that would be bearer-only.
After all if you go that way you don't have to worry about tokens signature or anything like that. This is handled automatically according to the scheme, flow and parties involved. I believe that by incorrectly making use of the flows you end up having to deal with tricky token issues. According to me that is the root cause and it will be more helpful than focusing on the signature problem. What do you think?
Did I miss something or am I completely wrong...?
You tell me.

Am I doing this whole API, client app, Oauth/OpenId Connect thing right?

I have some programming experience, but only with PHP and Java enterprise systems. But now I have some ideas about a web app in my new job. Since I am new at this, I would like to share how I have done the whole API in a server, browser app and authentication with Google’s OpenID Connect (I read a lot about Oauth and OpenID Connect, most helpful source was this: https://developers.google.com/identity/protocols/OpenIDConnect).
Server: Laravel - hxxps://coolapp-api.mycompany.com
Client: Angular - hxxps://coolapp.mycompany.com
TL;DR version:
1) User goes to hxxps://coolapp.mycompany.com, gets an Angular app login page. Types in their email, clicks “Sign in with Google”;
2) The app sends the email to hxxps://coolapp-api.mycompany.com/api/sign-in. The server redirects the user to hxxps://accounts.google.com/o/oauth2/auth with all the needed parameters;
3) The user logs in to their Google account, gives my app permission if it’s their first time, and Google redirects them to my server at hxxps://coolapp-api.mycompany.com/sign-in/google/callback. The server checks everything, and if it’s all correct, it creates a JWT token and send a redirect to the client app at hxxps://coolapp.mycompany.com/login/callback?token=JWT-TOKEN
4) The client app gets the token, stores it in local storage, and sends it to the server with every API call
More detailed version:
1) User goes to hxxps://coolapp.mycompany.com, gets an Angular app login page. Types in their email, clicks “Sign in with Google”;
2) The app sends the email to hxxps://coolapp-api.mycompany.com/api/sign-in. The server creates a state token and stores it in cache, associated with the email received. Then the server creates Google’s oauth URL and sends it to the client in the response body. I tried to do it with a HTTP redirect, but Google’s server was responding with an CORS error. The Angular app reads Google’s url from the response and goes there.
3) The user logs in to their Google account, gives my app permission if it’s their first time, and Google redirects them to my server at hxxps://coolapp-api.mycompany.com/sign-in/google/callback?code=AUTHCODE&otherstuff. The server sends the code it received (and all the other needed parameters) to hxxps://accounts.google.com/o/oauth2/token. It receives a id_token with that user’s email and basic info. This app is not public, so I don’t want anyone with a Google Account logging in, only the clients whose emails I added to the server database. So now the server checks if the user’s email in the token is in the database. If it’s not, it sends the user a HTTP 401 - Unauthorized. Then the server checks the state token in it’s cache associated with the email received. If it’s equal to the one received with Google’s redirect, then the server creates another JWT token, but now signed by my server. Finally, it sends a HTTP redirect to hxxps://coolapp.mycompany.com/login/callback?token=JWT-TOKEN with the new token.
4) The client app gets the token, stores it in local storage, and sends it to the server with every API call
Some comments:
Everything is HTTPS;
I added the strictest CSP policies I could to my Laravel server and Angular client;
Currently the app only supports Google’s sign in, while it is in development. Later on I’ll add more.
I made that my server only checks if the user’s email is in the database after they logged in with google because I like that idea that a non-authorized user should have no information about anything. If I made that check before it, during the first round trip, anyone could type an email and discover if that email has an account in my system;
On the last step, when my server sends the JWT token to my client app, I tried sending the token within a cookie, but since my API and my client app have different domains, my client app couldn't read the token. Sending it in the url was the only solution I could find. I tried logging in a popular app that uses Oauth and they did it this way too.
So my question is:
Am I doing something wrong, unsecure, weird?
Thank you all very much
1) Entering an email address every time a user wants to log in is tedious. And it's not needed if the user is already logged in at Google. The user should just click the "Log in with Google" button and get logged in without entering anything. The state parameter can be a random string - not related to the user's email in any way.
2) If you want your backend to process the redirect from Google (using the auth code flow - the backend has the client role in OAuth2 terms), the backend should also initiate a redirect to Google - not by sending data containing the redirect URL. To achieve it, after clicking the "Log in with Google" button, perform a whole page navigation (instead of an XHR request) to /api/sign-in and if the backend returns HTTP 302, the browser will correctly redirect to Google.
3) You should perform request validation (the state parameter) before getting tokens and checking whether the user exist.
On error (access denied), you can consider redirecting the user to an error page with error details instead of returning HTTP 401, since the HTTP code will cause a generic error screen to be displayed to the user. If you want to keep using HTTP codes, I think HTTP 403 Forbidden would be more appropriate.
4) Consider using sessionStorage instead of the localStorage. The sessionStorage gets cleared after closing a browser/tab and it's not shared among tabs. It makes it safer and it allows users to use different identity in different browser tabs.
The tokens your backend issues, is their validity time limited? Is the user required to get a new token after some (short) time period? If not, valid token vales may stay in the localStorage and browser's page history, which can be a security problem.
You can consider using your own OAuth2 auth server (such as RedHat Keycloak) which would accept Google (and later some other providers) for authentication and it would also issue access tokens accepted by your backend.

Is there an OpenID Connect grant type or mechanism for an app to poll for the auth-code when redirect_uri doesn't apply?

If you have an on-device application (e.g. desktop program, mobile device app) you can use OpenID Connect with some caveats:
Using Resource Owner Credentials (grant_type: password) is the simplest, but might not be possible if the authentication server operator won't let you use that grant-type because of trust reasons (i.e. they don't want you collecting the user's username+password yourself) - or if they have a dynamic or custom authentication UI that would be hard to replicate in a native app.
With the interactive flows (implicit, hybrid) the authentication sever's authentication page is shown in an in-app web-view. Most users will have no idea that the application can snoop on the authentication page and capture their username and password, especially on mobile devices - but this way the application code can easily capture the authorization code and/or access token, and automatically dismiss the web-view without any additional user interaction. (I'm surprised I haven't heard of more cases of users' details being captured by malicious apps this way.)
...so the advice is to always open the authentication page using the system's web-browser, but on the Windows desktop there is no good, standard way for the system web-browser to return the server response to the application code, though there are a number of approaches currently in use:
The authentication success page instructs the user to copy and paste a blob of text (containing the authorization code or access_token response) back into the desktop application.
Show the page in an app-hosted web-view, as per the notes above.
If the authentication process always only needs a username and password (for example) the application could still capture the user's username and password with its own UI and then make its own HTTP requests to make it seem like a user's web-browser session, and get the authorization code and/or access_token that way.
On Windows only:
Have a small utility program authHelper.exe that when invoked forwards its command-line arguments to a named-pipe in the user's session.
The main client-application will register authHelper.exe as a temporary URI scheme handler in the per-user HKCU\Software\Classes key, e.g. my-application: such that the contents of any my-application: URI are passed as arguments into authHelper.exe.
The URI passed to the system web-browser to open the authentication page has the redirect_uri parameter set to my-application:, so after the user authenticates in the browser, the browser will request the custom URI scheme which is handled by Windows, which invokes authHelper.exe "access_token=..." which then sends the data down the named-pipe to the running application.
If the user doesn't have permission to write to their own HKCU\Software\Classes key, or if they're using a version of Windows that doesn't support custom URI scheme handlers with EXE registrations then this doesn't work.
Windows UWP applications can also use the Web Authentication Broker.
I was wondering if a different approach could be used: why can't the application simply poll the authentication server for the status of the authentication attempt? Or does this approach already exist, and if so, what is the name of the flow or grant?
Here's the flow I'm proposing:
When the user wants to authenticate, the application opens the system web-browser as before, but with another parameter for a one-time-use opaque ID provided by the application.
As soon as the system browser is open, the application makes requests every 500ms or so (i.e. a polling loop) to the authentication server using its own HTTP client that asks for the status of the active authentication attempt associated with the same opaque ID as before.
The initial few responses from the authentication server to the application will presumably be status: pending, but eventually after the user successfully authenticates within a timeout window then the application's poll request would indicate a successful attempt and also contains the access_token or authorization code as is applicable. If the user failed to authenticate (e.g. 3 incorrect attempts) or left the window open long enough causing a timeout then the poll response would indicate failure.
Does this already exist and does it have a name? Are there any potential security risks or vulnerabilities with this approach?
It exists and has a name, "OAuth 2.0 Device Flow for Browserless and Input Constrained Devices", but is not yet fully standardized, see: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-device-flow
Google also implemented this flow avant-la-lettre in a vendor-specific way:
https://developers.google.com/identity/protocols/OAuth2ForDevices

Does the Box API OAUTH protocol work from behind a firewall?

I am investigating the possibility of integrating Box API calls from an internal application that sits behind our firewall (it is not exposed to the outside world).
So the question is, if we fire off an authentication request to Box with a callback URI, does Box post back directly to the specified call back URI (so essentially its initiating a new request from Box to the client), or does it send a request back to the client who made the request (standard HTTP request/response), and expect the client to redirect to the call back URI with the tokens?
This might sound an odd question, but during my investigation it appears this is how the Twitter OAUTH protocol works, and if so would help us a lot as we don't want to open up the firewall to the outside world.
See here for info: https://dev.twitter.com/discussions/5801
EDIT: Just found this which seems to suggest that the client will always initiate the request, never the server: https://stackoverflow.com/a/6116736/811108
Many thanks in advance.
A typical user journey for OAuth on Box would like like this:
User's browser requests www.someboxapp.com and the user clicks a login button
User's browser requests the Box authentication URL which begins with https://www.box.com/api/oauth2/authorize
User authenticates on the Box authorize webpage and then the Box site sends a 302 redirect header back to the users browser. This header tells the user's browser to request the the redirect_uri configured by www.someboxapp.com
User's browser requests the redirect URL on www.someboxapp.com - e.g. http://www.someboxapp.com/oauth/redirect_uri
The Box Application running on www.someboxapp.com makes a POST request to https://www.box.com/api/oauth2/token to complete the authentication and get an access token for using the Box API on behalf of the user.
What this means is that if you are running a Box web app on your internal network - you need to make sure that the webserver running the application and the users' machines can connect to https://www.box.com/api/oauth2/
If www.someboxapp.com only exists on your local network - that is fine - the Box API does not need to connect to this host.

Any way to test OAuth integration to google without having to have a port forwarding rule in my router back to my dev PC?

Any way to test OAuth integration to google without having to have a port forwarding rule in my router back to my development Mac?
Background:
Developing Ruby on Rails web app that will request data from Google Calendar API via OAuth
Using Rails "OAuth Plugin" for this purpose
Developing on MacBook which is running my dev environment
Google needs (as part of OAuth) to make a callback back to my local dev environment
Can't seem to think of a way to test with Google without having to set a Port forwarding rule in my site's router back to my Macbook?
This threw me for a loop too. I actually went through the work of setting it up on a public IP even though I didn't have to in the end.
But yes, your comment is correct. The callback URL is passed on to Google during the redirect then after you've been authenticated with Google, Google will redirect you back to the callback URL providing an authorization code as a parameter.
The only server to server communication that happens is exchanging the authorization code for access and refresh tokens. This typically happens on the callback page. But since it's initiated by your server and not Google, no special open ports are required.

Resources