What is the correct way to configure Identity Server 3 for authorization code flow with SPAs? - asp.net

We have an instance of Identity Server 3 which has been used for some time with various clients, some using implicit flow, others using client credentials. We now have a new requirement to integrate an iOS native app with this identity provider. I understand these days implicit flow is not recommended and public facing apps should instead be using authorization code flow. Examples of such advice are here and here.
By my understanding, authorization code flow has a step whereby a received authorization code is exchanged for JWT tokens via some back channel by supplying it alongside a client ID and secret. However, with SPAs and native apps we don't have the luxury of storing secrets. The guidance I found here would suggest I can simply omit the secret from the connect/token request, but my testing so far doesn't confirm this. So I'm stuck. To demonstrate, I've set up a client on my local instance of IS3 to test with:
{
'clientId': 'test',
'flow': 'AuthorizationCode',
'name': 'test',
'redirectUris': [ 'http://localhost:8080/' ],
'scopes': ['openid','profile']
}
I then make the following GET request to my IdP:
[ID_PROVIDER]/connect/authorize?client_id=test&redirect_uri=http%3A%2F%2Flocalhost%3A8080&scope=openid%20profile&response_type=code
This lets me sign in and returns me to my test app running at http://localhost:8080 with my authorization code in the querystring.
I now try to exchange this code for JWT tokens by POSTing to [ID_PROVIDER]/connect/token with the following body: code=[AUTH_CODE]&grant_type=authorization_code&client_id=test&redirect_uri=http%3A%2F%2Flocalhost%3A8080
But Identity Server rejects this with an HTTP 400 and invalid_client error. When I dig into its logs I see a ClientSecretValidator event with message "No client secret found". Which kind of makes sense based on my understanding outlined above, but given people are recommending using this flow for public-facing apps I must be misunderstanding something.
If anyone could clarify that'd be great, thanks.

You can't just omit the client secret. For your native case, I'd consider embedding the secret within the app. The authorize request will still have to validate the return_uri (custom URI scheme for your native app) and if that still feels insecure, you can also lean on Proof of possession (PoP) tokens (https://identityserver.github.io/Documentation/docsv2/pop/overview.html).
For a SPA app I would keep it implicit flow, I see no point in doing secrets there.

Related

Is there a mistake in the token validation documentation? Or am I getting this wrong?

In this section of the sign-in guide,
https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library,
There is this code snippet
# Specify the CLIENT_ID of the app that accesses the backend:
id_token.verify_oauth2_token(token, requests.Request(),CLIENT_ID)
This correctly validates the token when I pass my server's client ID, but fails when I pass the android app's client it.
However, the comment suggests that it should be the android app's client ID that should get passed here! And that also makes more sense from a security perspective.
Further down, I can find the following Python code sample
# Or, if multiple clients access the backend server:
# idinfo = id_token.verify_oauth2_token(token, requests.Request())
# if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
# raise ValueError('Could not verify audience.')
I have played around a bit and it seems to me that idinfo["aud"] key carries the client ID of the server, where as idinfo["azp"] carries the client ID of the client.
If I understand it right, in this line we are supposed to verify the client id of the android app client!
So it should read:
if idinfo['azp'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
raise ValueError('Could not verify audience.')
I feel like I must be getting something wrong here, but what is it?

does "app_key" means the same as "app_code" for HERE geocoding credentials on Python?

I am trying to geocode some addresses with python
so I created a freemium account on https://developer.here.com
my code is this
(...)
here = Here(here_config.app_id, here_config.app_code, timeout=5)
here.geocode(string_to_geocode)
I am getting the following error message:
HTTP Error 401: Unauthorized
my doubt comes from the difference between the terms "app_code" on my code sample and "app_key" on my credential screen.
Is there another step I need to do in order to get a "app_code" or is my "app_key" already supposed to be it?
P.S. on that same screen Here provides me an option of getting JAVASCRIPT keys, HERE SDK FOR IOS (PREMIUM EDITION) [this option cleary has a button that says "generate app_key and app_code", however, I am not developing a cellphone app, but a python program.
What am I missing ?
here_credential_screen
App ID and App Code have been replaced. We encourage all users to switch to API Key or OAuth 2.0 (JSON Web Tokens) authentication. Please be aware that as part of adapting to the new authentication method, some endpoints have also changed.
please check the new domains here
You can either use your ApiKey or App_Id/App_Code.
for example like this-
https://geocoder.ls.hereapi.com/6.2/geocode.json?apiKey={YOUR_API_KEY}&searchtext=425+W+Randolph+Chicago
https://developer.here.com/documentation/geocoder/dev_guide/topics/quick-start-geocode.html

How to verify a HS256 signed JWT Token created with Keycloak authentication provider on jwt.io

I am trying to verify a HS256 JWT Token generated with locally ran KeyCloak Authentication Provider on https://jwt.io.
The KeyCloack instance is running on my local machine inside a docker container. I have applied almost the same steps as described in this answer (which on contrary applies the RS algorithm instead, and works as described): https://stackoverflow.com/a/55002225/1534753
My validation procedure is very simple:
1.) Request the token (with Postman) from my local docker KeyCloak instance with:
POST requesting http://localhost:8080/auth/realms/dev/protocol/openid-connect/token
2.) Copy the token contents inside the jwt.io's "Encoded" section
3.) I verify that the header and payload are as expected and correct
4.) I copy the client secret from my KeyCloak instance admin dashboard, you can see the reference on the image below:
5.) I paste the secret into the "VERIFY SIGNATURE" section on jwt.io and the "Encoded" token section changes, hence resulting with an invalid signature and a invalid (i.e. different) token.
My core question is what am I missing here? Why does the token change when I apply the expected secret!? Am I applying the right secret, the one from the client? If I understand JWT infrastructre and standard correctly then It should stay the same if the secret (with the expected algorithm applied) is valid. My reasoning is that something with JWT creation on KeyCloak is specific. I have not touched the HS256 algorithm provider on KeyCloak, everything is used as default with the docker installation guide on using KeyCloak. The settings related to the token and algorithm are setup to use HS256, and the algorithm is specified as expected in the JWT's header section correctly which can be verified after the encoded token is pasted into the jwt.io's page.
I need this to work as I am trying to apply the same JWT validation process inside a .NET Core web API application. I have encountered this whole issue in there, i.e. inside the System.IdentityModel.Tokens.JWT and the JwtSecurityTokenHandle.ValidateSignature method which results with an invalid signature and finally resulting in an exception.
On side note, I am accessing the token with Postman and its Authorize feature the configuration can be seen on the image below:
One more side note is I have a user "John" which belongs to my "Demo" realm. I use him to request an access token from KeyCloak.
To get the secret used for signing/verifying HS256 tokens, try using the following SQL:
SELECT value FROM component_config CC INNER JOIN component C ON(CC.component_id = C.id) WHERE C.realm_id = '<realm-id-here>' and provider_id = 'hmac-generated' AND CC.name = 'secret';
If you use the resulting secret to verify the tokens, the signature should match. I’m not sure if this secret is available through the UI, probably not.
Source: https://keycloak.discourse.group/t/invalid-signature-with-hs256-token/3228/3
you can try using Keycloak Gatekeeper.
If you want to verify that token in that way you need to change the Client Authenticator to "Signed JWT with client secret", otherwise you can use this "Gatekeeper" option. Here you can read more about it.

Connecting HubSpot to SQL Server over API

Pretty stumped at this point, hopefully someone has been able to figure out this problem before. I'm trying to create a process that will synchronize my user data from HubSpot and SQL Server (collected through my web app). This would involve me being able to write into HubSpot from SQL Server or vice versa. In order to do that I need to use their API and I'm having issues connecting to the API itself.
I was able to get the connection working with the Google OAuth 2.0 Playground and extract the customer data (so I know they work), but I want to create an equivalent connection R. From the research I've done so far, here's what I think may be the best options:
Externally: I found a company called Zapier that apparently can do this if I pay for their services, I have never used them
Inhouse: Using ROAuth or httr packages, but I couldn't authenticate successfully. I've tried:
reqURL<- 'https://api.hubapi.com/contacts/v1/lists/all/contacts/all'
accessURL<- "Couldn't figure out?"
authURL<- 'https://app.hubspot.com/oauth/authorize?client_id=[my client id]&scope=contacts%20automation&redirect_uri=https://[mywebsite]'
cKey<- 'my hubspot app client id'
cSecret<- 'my hubspot app client secret'
credentials<- OAuthFactory(consumerKey=cKey,
consumerSecret=cSecret,
requestURL=reqURL,
accessURL=accessURL,
authURL=authURL)
Also tried:
curl('https://api.hubapi.com/contacts/v1/lists/all/contacts/all/hapikey=[my hapi key]/get')
Helpful Links:
Testing the API in Google playground: https://developers.hubspot.com/docs/faq/testing-hubspot-apis
Authentication Overview
https://developers.hubspot.com/docs/methods/auth/oauth-overview
Fields:
Authorization Endpoint: https://app.hubspot.com/oauth/authorize
Token Endpoint: https://api.hubapi.com/oauth/v1/token
Client ID: ClientID
Client Secret: SecretID
I also have a Hapi key and App ID, but not sure if they're required
Really appreciate the help!
Cheers
After some digging, I was able to connect using the HAPI Key, rather than doing OAuth. It's actually pretty simple:
library(httr)
library(jsonlite)
hs_data<- GET(paste("https://api.hubapi.com/contacts/v1/lists/all/contacts/all?hapikey=",{yourapikey})
hs_data<- content(hs_data, as='text')
hs_data<- fromJSON(hs_data)
hs_data <- hs_data$contacts$properties
Some things that were messing me up previously:
Make sure to use your personal HAPI key, not the account (if you're admin) HAPI key
Make sure only Contacts scope is checked in your app, it doesn't work with more than 1 scope clicked.

Endpoint / API Key for Microsoft Academic Knowledge

I'm trying to use the MS Academic Knowledge API. I signed up for keys here as per the docs
https://labs.cognitive.microsoft.com/en-US/sign-up
When I use the key I get errors as follows
api.labs.cognitive.microsoft.com:
Endpoint api.labs.cognitive.microsoft.com is not supported
westus.api.cognitive.microsoft.com:
'Access denied due to invalid subscription key. Make sure you are subscribed to an API you are trying to call and provide the right key.'
I'm not sure what is going on here and which endpoint I need to use
The following works for me:
https://api.labs.cognitive.microsoft.com/academic/v1.0/interpret?query=darrin%20eid&complete=1&count=10&model=latest&subscription-key=your_key
(replace "your_key" with your labs subscription key)
Additionally, you can see the URL you need to use for each different API when you use the "try it" test site

Resources