I am developing a Node.js web app that uses Keycloak as authentication service. I already have two clients: public client for the web app (app-web) and bearer-only for the API (app-api). On the app-api I use resources, scopes, policies, and permissions to control the access.
To check the permissions, I am using the keycloak.enforcer(...) from the keycloak-connect module (npm keycloak-connect). When I try to check permission, the server always returns 403 Access denied response. But if I change app-api from bearer-only to confidential (keeping the same keycloak.json configuration file), the client works fine and is capable to check permissions.
This problem seems to be because a bearer-only client cannot obtain tokens from the server (keycloak similar question).
My question is: Is this a normal behavior of Keycloak? Why allow the Authorization tab in bearer-only clients if you cannot use the keycloak.enforcer? Am I missing some configuration?
Thanks for your help.
I also stumbled over this bearer-only thing a few days ago. And I also found it confusing that you can enable fine grained authorization, it took me some time to find that out too. Well, a bearer-only client, as the name already states, authenticates clients only by their bearer token.
I posted this question to the Keycloak mailing list too and the answer was:
It is the expected behavior but also a UI issue. You should not have access to that tab when the client is bearer-only.
They even create an issue KEYCLOAK-10808
This is similar to what #Valentin mention.
If you want to read all the thread: Mailing list response
Related
I have an Electron app that requires access to the users Google Drive and I want to implement the api functionality without having to expose the client secret. From my understanding, this is impossible to do in certain scenarios like mobile applications, but what is the proper way of going about this on a local app?
When trying to follow the web-app OAuth instructions from Google, it looks like you can't use this method on a local application. When trying to setup the OAuth process this way it doesn't even let you whitelist localhost as a domain to authenticate users on (which breaks the process since this is a local app running on Electron). Add on to that this paper that Google released and it also seems like you can't trick the auth process to think it's not running on localhost, and you also can't run Node.js in the browser (I'm using Electron so this is impossible to do).
I then tried following their Mobile and Desktop app workflow which seemed promising. The issue arises when you need to Exchange authorization code for refresh and access tokens. This again requires that you show your client secret in your main app. I then though of splitting this up and doing some of it locally and then having an auth server that held the client secret and exchanged the authorization code from the client and returned a refresh and access token. Looking at the diagram that Google provides for visualizing this process, it clearly shows that your app needs to do both parts of the authorization process so that idea was also out.
One application that I personally use and looked at was rclone and from the looks of it they just list their client ID and secret directly in their code. The client secret is encrypted, but if you follow the workflow it gets revealed with a key that is also just stored locally on the app. So it's plain text is obscured, but there is nothing preventing anyone from getting hold of the client secret by slightly modifying the code.
I should also mention this app is in a public repo on GitHub and will stay that way.
This is my first time using OAuth so I may be misunderstanding something, but I tried following the documentation as closely as I could and can't shake the feeling that I'm overlooking a piece of this process.
And if the only way to solve this problem is to expose both the client id and secret, is there any way this could lead to users data being compromised? Since the Google Drive API is free to use I don't really mind if others use some of my quota. I'm more worried about security.
For public clients like Desktop apps you're developing, you'll need to use the PKCE flow. You're right that Google's documentation seems off here - you shouldn't need to pass the client_secret as part of the authorization code exchange.
That's supported by the documentation here: https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange/
It's possible that Google requires the client_secret but it doesn't treat the parameter as a real "secret" for public clients, but rather an additional identifier that is not sensitive, and not sufficient on its own to do anything on behalf of your application. Section 8.5 of the specification reads:
Secrets that are statically included as part of an app distributed to
multiple users should not be treated as confidential secrets, as one
user may inspect their copy and learn the shared secret. For this
reason, and those stated in Section 5.3.1 of [RFC6819], it is NOT
RECOMMENDED for authorization servers to require client authentication of public native apps clients using a shared secret,
as this serves little value beyond client identification which is
already provided by the "client_id" request parameter.
Authorization servers that still require a statically included shared
secret for native app clients MUST treat the client as a public
client (as defined by Section 2.1 of OAuth 2.0 [RFC6749]), and not
accept the secret as proof of the client's identity. Without
additional measures, such clients are subject to client impersonation
(see Section 8.6).
You might also look into standalone OAuth service providers, like Xkit where I work. That would let you keep the secret confidential while still going through an OAuth flow.
We are testing the use of Docusign, within the approval flow of a business application.
We are using the OAuth solution for Service integrations, the JWT (JSON Web Token) flow.
When I call the apiClient.ConfigureJwtAuthorizationFlow method in the aplication hosted in IIS Localhost, I obtain the OAuthToken and can send envelopes.
But when I deploy the solution on the Test server, with the same userId, integratorKey and privateKey, the call to apiClient.ConfigureJwtAuthorizationFlow method return null.
I did:
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=XXXXXXXXXXXX&redirect_uri=https://www.docusign.com
Do you have any idea how to fix this? What's the matter?
Have you tried using the C# .Net Core or C# .Net Framework examples? See if one of those examples work on your Test server. If they do, the problem is in your code. If they don't then they are the first issue to be solved.
As you say, there should be no difference in where you run your application.
The usual issues to be checked for a test server: Firewall rules, DNS configuration, SSL root certificate store, etc.
Also, the url you gave is used for manually granting consent. It is only needed once per user (on demo). You only use that URL once to grant consent. After that, the JWT token can be used to get an access token which impersonates the user.
(Tom Vaga asked a similar question here but Luke's response didn't quite address what I'd hoped to accomplish... I'd comment there but don't have the points yet :-) Thanks! )
I've got a Slim server working well to register and authenticate users for our API, using JWT, allowing only 'authenticated' users to access certain api endpoints.
I'm now trying to setup a SocketCluster for various realtime messaging parts of the app, and I would like to restrict subscriptions to only authenticated users. I may be missing a part of the concept, but is it not possible to use the token-cookie set successfully by Slim to also authenticate to SocketCluster? (ideally using the built-in authentication process, and without having to call-back to the slim-api?) They're on different servers as sub-domains... Would I have to insert the same secret into the SocketCluster configuration somewhere?
Thank you!
I'm asking this question on the back of a previous question I raised, as the scope of the question has changed somewhat but that might be worth reading first for background info.
I'm trying to programatically obtain data out of our Dynamics CRM instance, using a single set of admin credentials within a Node powered Express app. This Express app is hosted on a separate server outside of our network where CRM is hosted. The app will then request, process and serve CRM data back to any logged in user who has access (controlled by roles/permissions within the app), meaning an end user only has to login into the Express app.
From my web browser, if I visit our on-premise CRM endpoint: https://my.crm.endpoint, I get prompted for a username and password.
If I provide correct credentials, I am authenticated and have full access to the CRM, allowing me to query the API.
Example
https://my.crm.endpoint/api/data/v8.2/contacts?$select=fullname,contactid
This returns a lovely JSON object containing all the data I want :)
NOW! Under the covers, I can see that it is using NTLM to authenticate, of which I have little knowledge :/ Having read up a little and watched a few YouTube videos, I have a basic understanding of the challenge/response mechanism but I'm still unsure as to how to proceed.
NB: I have read this from Microsoft which describes the mechanism, but doesn't give any specific examples. I don't even know what hashing algorithm should be used, or what headers to set etc.
Question Can anyone provide any sort of detail as to how I can authenticate with our CRM using NTLM from a Web App (Express in my case)?
Steps I can see the browser making...
Visit https://my.crm.endpoint
302 Redirected to: https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wctx=rm%3d1%26id%3dfaf0791c-6a3a-4c4e-9e69-9dfa8fd4c2e8%26ru%3d%252fdefault.aspx&wct=2018-04-20T10%3a12%3a37Z&wauth=urn%3afederation%3aauthentication%3awindows
Prompted for user credentials
** enter credentials**
A whole bunch of stuff happens here and I get a little lost but looks like it gets a couple of 401's then a POST is made to the https://my.crm.endpoint. Another 302 is shown, then finally a GET to the actual default.aspx page.
I then have access to CRM.
NB: Once authenticated, I can see three cookies that have been set and which are sent when querying the api example above. These cookies are MSISAuth, MSISAuth1 and ReClientId.
If I'm missing any crucial info, please let me know and I'll provide what I can!
UPDATE
I have just installed httpntlm module and attempted to authenticate using this...
let httpntlm = require('httpntlm');
httpntlm.get({
url: 'https://my.crm.endpoint',
username: '<my.email#address.com>',
password: '<mypassword>',
workstation: '', // unsure what to put here if anything?
domain: '' // unsure what to put here if anything?
}, function (err, res){
if(err) return err;
console.log(res.headers);
console.log(res.body);
});
The response I get is this...
{ location: 'https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wctx=rm%3d1%26id%3d93a4c6fd-5b17-4a2b-965f-07af5e96b08f%26ru%3d%252fdefault.aspx&wct=2018-04-20T14%3a08%3a00Z&wauth=urn%3afederation%3aauthentication%3awindows',
server: 'Microsoft-IIS/8.5',
req_id: '298acefc-53aa-46fa-96c4-e5d8762b1fd2',
'x-powered-by': 'ASP.NET',
date: 'Fri, 20 Apr 2018 14:08:00 GMT',
connection: 'close',
'content-length': '397' }
<html><head><title>Object moved</title></head><body>
<h2>Object moved to here.</h2>
</body></html>
Anyone able to shed any light on what I actually need to be doing?! :-/
UPDATE 2
Following #markgamache comment, and having read the suggested docs, we are indeed using WS-Fed! As the Wa=signin1.0 parameter informs the browser to pop up a login box, does this make it impossible to achieve this programmatically, without additional user interaction?
Based on my following understanding:
You are using CRM on premises with claims authentication (ADFS). This means that when an user accesses CRM, the user is redirected to ADFS for authentication (if the user is in the internal network, by default ADFS uses integrated Windows authentication) and then the user is redirected back to CRM.
You must call a CRM endpoint from an external (node.js) application. That call is NOT "client side" (i.e. via browser/javascript) but "server side" (i.e. from the web server hosting the application)
The ideal solution would be to apply here the S2S (server to server) scenario which involves an application user in CRM which in turn is used to call a CRM API using the OAuth client credentials flow (client id + secret). Problem is, as far as I know, currently the application user concept is only supported in CRM online, not on premises.
So then you can try one of these 3 options:
Despite the fact that you are using claims authentication in CRM, you can still use integrated Windows authentication (IWA). How? If you check the CRM IIS site, you must have an HTTPS binding. If you add an HTTP binding (i.e. port 80 no host header), you can access http://CRM_Server_Name/api/data/v8.2/contacts using IWA. So in this scenario, the httpntlm module you already tried could work. Please note that CRM supports one HTTPS IIS binding and one HTTP IIS binding - so make sure to not to add more of one binding of each type.
Mimic (programmatically of course) the authentication flow you observed in the browser. What does this mean? Generate an IWA authenticated request to https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wauth=urn%3afederation%3aauthentication%3awindows. ADFS will authenticate you and will give you some cookies. You will need to store those cookies to make a subsequent request to https://my.crm.endpoint/api/data/v8.2/contacts. Not a great solution but should work.
Use OAuth. Problem is that as I described at the beginning, the ideal OAuth flow for this scenario (client credentials) is not available for CRM on prem as far as I know. So then you must use the Authorization Code Grant flow, described here. First you will need to register an ADFS application and then again, you will need to make several HTTPS calls (one of them will be a call to the ADFS IWA authentication endpoint) to finally obtain a token that you can use to make calls to the CRM endpoint.
that is a redirect to an ADFS server for claims based authentication. That page, or the next redirect, will ask you to authenticate with either cert, forms, or windows integrated (NTLM or Kerberos). If you pass auth, your browser will be given a token to send to https://my.crm.endpoint. The URL suggest the claims will be via WS*. https://blogs.technet.microsoft.com/askpfeplat/2014/11/02/adfs-deep-dive-comparing-ws-fed-saml-and-oauth/
I've been doing some work with OAUTH 2 in the last few years. I have a few authorization servers and several clients using them.
Anyone who has made an app or some client solution that uses OAUTH2 knows that the client secret can be a problem. One can mitigate this somewhat by using access code grant. There has been talk about using a proxy for the secret.
My question is about the redirect URI - this was meant to protect the Access Code grant process. The Auth server will only return the access code to the redirect that is on file (in the database for that client ID). The issue comes into play with mobile apps. They usually depends on https://127.0.0.1 or https://localhost for a redirect URI. Anyone can get a localhost token, right?
With this being the case, what is stopping someone from impersonating a clientID using the localhost redirect URI? Could I not make a copy-cat app, use the same OAUTH2 sign in flow using the real client ID and if I was able to get a user to login, I now have an OAUTH token to access resource servers with. Am I wrong?
If anyone can shed more light on this, I would be greatly appreciative. I want to learn as much as possible, anything helps.
The attack that you describe is a known weakness when using the Authorization Code grant for native mobile apps. An OAuth 2.0 extension called "Proof Key for Code Exchange" has been developed to mitigate against this threat through the use of a "code verifier" that is dynamically generated and only known by the real Client.
This work was standardized in the IETF as an RFC called Proof Key for Code Exchange by OAuth Public Clients, available at: https://www.rfc-editor.org/rfc/rfc7636