Analyzing oAuth2 client credential flow with oAuth2-proxy, Keycloak / oAuth2-proxy logging - oauth-2.0

My application running on Kubernetes (AKS) has a working standard oAuth2 authentication flow, which I added using oAuth2-proxy and Keycloak. The password Credentials grant type / standard flow via the Browser is working fine. After the redirect to the KC login page and manual login, the oAuth2-proxy lets the user pass and and application page (echo server) is shown.
Now I am trying to use Grant type client credentials, e.g from Postman or Curl. I have enabled 'Service Accounts Enabled'. I can retrieve the access_token / bearer token without issues and am including it in the header "Authorization'. I can see that the token is valid and other contents also looks correct, but the request does not pass. The oauth2-proxy redirects the request to the login page.
oAuth2-proxy parameters:
- --provider=keycloak-oidc
- --client-id=nginx
- --client-secret=topsecret
- --redirect-url=https://my-redirect-url
- --oidc-issuer-url=https://myurl
- --silence-ping-logging
- --auth-logging=true
- --session-store-type=redis
- --set-xauthrequest=true
- --set-authorization-header=true
- --pass-authorization-header=true
- --pass-access-token=true
- --insecure-oidc-allow-unverified-email
- --show-debug-on-error
- --errors-to-info-log
- --cookie-secret=gf...
- --cookie-httponly=false
- --force-json-errors
I am not sure if need to include this script in the Ingress or not:
# nginx.ingress.kubernetes.io/configuration-snippet: |
# auth_request_set $name_upstream_1 $upstream_cookie__oauth2_proxy_1;
# access_by_lua_block {
# if ngx.var.name_upstream_1 ~= "" then
# ngx.header["Set-Cookie"] = "_oauth2_proxy_1=" .. ngx.var.name_upstream_1 .. ngx.var.auth_cookie:match("(; .*)")
# end
# }
Candidate root causes:
the token does not contain what is checked (KC config...)
the token is not included in the request in the right way
the oAuth2-proxy config is missing something to get the token correctly from the request
oAuth2-proxy cannot validate the token against Keycloak
I can access the Keycloak, Nginx and oauth2-proxy logs. So far the oauth2-proxy logs helped to solve earlier issues, but the whole time I am missing a better what to analyze what is going on. I wish there was a trace log level on oAuth2-proxy which I can enable via an env var using my Helm values file, but the available options in the doc did not seem suitable.
What am I missing? How can I analyze this issue better? Or already any suggestions about the root cause / fix?

You need to enable resource server mode (--skip-jwt-bearer-tokens = true flag).

Related

Is it possible to set the Micronaut OAuth2 callback-uri as an absolute URL?

I have a Micronaut web-app that uses OpenId / OAuth2 / JWT. In some environments, everything works really well with this set up, however, in other environments, auth fails during the step where the configured callback-uri is called. For some reason, in these environments, the URL generated is "http" instead of "https". This causes the call to fail since my application is only accessible over https.
I have no clue why it is trying to use http in the first place, however, if I was able to specify the callback-uri as an absolute / full URL, then I could probably work around this anomaly in these environments.
An example yml config that I use:
application:
name: xxxxx
security:
authentication: idtoken
oauth2:
enabled: true
clients:
azure:
client-id: ${OAUTH_CLIENT_ID}
client-secret: ${OAUTH_CLIENT_SECRET}
openid:
issuer: https://login.microsoftonline.com/xxx
callback-uri: ${OAUTH_CALLBACK_URI}
redirect:
login-success: ${LOGIN_SUCCESS_URL}
logout: '/logout-handler/logout-success'
endpoints:
logout:
get-allowed: true
token:
jwt:
cookie:
cookie-same-site: none
cookie-secure: true
In this config if I set the callback-uri environment variable (OAUTH_CALLBACK_URI) to /oauth/callback/azure, for example, then the full URL that seems to be used is http://xxxxx/oauth/callback/azure. However, if I use a full URL for the environment variable, e.g. https://xxxxx/oauth/callback/azure then the full URL it uses still appends that as opposed to using it as an absolute URL, i.e. http://xxxxx/https://xxxxx/oauth/callback/azure.
Is it possible to specify this uri as an absolute one and not have it append it like the above effectively duplicating it?
Good news. This was fixed in micronaut-security 2.3.4
https://github.com/micronaut-projects/micronaut-security/pull/644

Keycloak Invalid token issuer

I have a mobile app(react-native), a resource service(spring boot) and Keycloak Authenticatioin Service(Auth-Service).
Client makes authentication directly with Auth-Service and gets the access token.
When I do a request to the resource service, the resource service checks the access token by asking to the Auth-Service. But token obtained by the client app and iss field is http://10.0.2.2:8060/auth/realms/sau and my resource service at http://localhost:8110.
Keycloak says: error="invalid_token", error_description="Invalid token issuer. Expected 'http://localhost:8060/auth/realms/sau', but was 'http://10.0.2.2:8060/auth/realms/sau'"
My question is how can I make authentication in resource service behalf my client?
Mobile App:
export const prepareRequestBody = credentials => {
const params = new URLSearchParams();
params.append('username', credentials.username);
params.append('password', credentials.password);
params.append('client_id', "nasilim-mobile-app");
params.append('grant_type', "password");
return params;
};
export const login = credentials => {
const params = prepareRequestBody(credentials);
return axios.post(LOGIN, params);
};
Resource-Service:
application.yml
keycloak:
realm: sau
resource: photo-service
bearer-only: false
auth-server-url: http://localhost:8060/auth
credentials:
secret: 69a3e80c-8360-42df-bcec-b6575a6949dc
Note: I have checked this question and I have tried to set "X-Forwarded-For" : "http://localhost:8060/" but It didn't work Keycloak returns:
{
"error": "invalid_request",
"error_description": "HTTPS required"
}
Here is a Sample Access Token that obtained by mobile client.
The "iss" claim vary in function of the request. The variable KEYCLOAK_FRONTEND_URL can change this behavior. So try do as follow in your docker-compose file:
KEYCLOAK_FRONTEND_URL: http://10.0.2.2:8060/auth
You need to configure access from your Spring Boot app to the Auth server in an external fashion, not localhost:
keycloak:
realm: sau
resource: photo-service
bearer-only: false
auth-server-url: http://10.0.2.2:8060/auth
credentials:
secret: 69a3e80c-8360-42df-bcec-b6575a6949dc
This way the token issuers will match. This will probably require either to disable SSL requirement for external request in keycloak or to configure proper SSL communication. If this is meant for production, do the right way.
See also:
https://stackoverflow.com/a/42504805/1199132
What if you can not access the auth server using the external address? This will not work. Check https://issues.redhat.com/browse/KEYCLOAK-6984
One workaround is to set the reaml public key. But it's not recommended as the adapter will not check for new key if the key is rotated.
Use the proxy-url in the adapter configuration to provide an alternative URL.
See docs.
Spring application.yml:
keycloak:
authServerUrl: http://10.0.2.2:8060/auth
proxyUrl: http://localhost:8060/auth
...
Or keycloak.json:
{
"auth-server-url": "http://10.0.2.2:8060/auth",
"proxy-url": "http://localhost:8060/auth",
...
}
At keycloak go to your realm then to realm settings and place the url you want at frontend url.
Problem is because inside "container" there is different ip(host).
Keycloak has property hostname-url. You should inform keycloak about your frontend(hosts). You can do that during starting new instance of keycloak. Command to start your keycloak is start-dev --hostname-url=url-to-your-frontend
you can provide the hostname of frontend, so the command will be look like this:
start-dev --hostname-url=http://10.0.2.2:8060
This solution is working for Keycloak version 19 and up

What scope is required to call Executionhistory - List

I am creating a dashboard extension, and would like to call Executionhistory - List.
Currently, the extension manifest specifies only the scope vso.serviceendpoint_manage.
I get my authorization token via
const token = await VSS.getAccessToken();
var authHeader = VSS_Auth_Service.authTokenManager.getAuthorizationHeader(token);
I am able to successfully get a list of all service endpoints by using Endpoints - Get Service Endpoints
However, when I try to get the Execution History for a given Service Endpoint, I get a CORS policy error.
Access to {devops rest api} from origin {marketplace url} has been blocked by CORS policy
I know CORS errors can occur if the PAT does not have the correct scopes see here.
Note: When I try this locally with a PAT with all scopes, this call works. So I am fairly certain I am missing an extension scope.
Which scope am I missing?
To get service endpoint execution history, no need to set anything other, just set PAT token scope as Read & manage to Tokens:
You will see it is work to run this Executionhistory - List api.

Can't deploy an app to Intune store via graph API - DeviceManagementApps.ReadWrite.All is an invalid scope?

We want to enable uploading apps to the Intune store via an API.
I saw this example on GitHub, and want to do something similar in JS, so I've tried using the same REST calls.
The problem is, I can't seem to make the https://graph.microsoft.com/beta/deviceAppManagement/mobileApps request properly - I always get 401. When making the same request via the Graph API Explorer it works fine.
I tried fixing my permissions, and I'm kinda stuck getting the correct token.
I did the following steps with an admin account, on both the "common" and our own tennant:
Called the admin consent - https://login.microsoftonline.com/nativeflow.onmicrosoft.com/adminconsent?client_id=<ID>&redirect_uri=<URI>
Got authorization from the user - https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=<ID>&response_type=code&redirect_uri=<URI>&response_mode=query&scope=DeviceManagementApps.ReadWrite.All
POST request to get the actual token -
https://login.microsoftonline.com/nativeflow.onmicrosoft.com/oauth2/v2.0/token
with the following body:
client_id: <ID>
scope: https://graph.microsoft.com/.default
client_secret: <secret>
grant_type: client_credentials
requested_token_use: on_behalf_of
code: <The code I got in step 2>
I tried changing the scope in step 3 to https://graph.microsoft.com/DeviceManagementApps.ReadWrite.All or simply to DeviceManagementApps.ReadWrite.All, but it says that it's not a valid scope.
I got a token in step 3, but when I try calling the actual API I receive this error:
{
ErrorCode:"Forbidden",
Message:{
_version: 3,
Message: "An error has occurred - Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 - Activity ID: 7b5c3841-976d-4509-b946-f7fdabd047d7 - Url: https://fef.msub02.manage.microsoft.com/StatelessAppMetadataFEService/deviceAppManagement/mobileApps?api-version=5018-05-02",
CustomApiErrorPhrase: "",
RetryAfter: null,
ErrorSourceService: "",
HttpHeaders: {"WWW-Authenticate":"Bearer realm=urn:intune:service,f0f3c450-59bf-4f0d-b1b2-0ef84ddfe3c7"}
},
Target:null,
Details:null,
InnerError:null,
InstanceAnnotations:[]
}
So yeah, I'm pretty much stuck. Anyone have any experience with it? I've tried making the calls in Postman, curl and via code, but nothing works.
Cheers :)
You have a couple issues going on:
You're using the Authorization Code Grant workflow but requesting Client Credentials.
The scope Device.ReadWrite.All is an application scope, it is only applicable to Client Credentials. It isn't a valid Delegated scope so it will return an error when you attempt to authenticate a user (aka delegate) using Device.ReadWrite.All.
Your body is using key:value but it should be using standard form encoding (key=value).
To get this working, you need to request a token without a user. This is done by skipping your 2nd step and moving directly to retrieving a token (body line-breaks are only for readability):
POST https://login.microsoftonline.com/nativeflow.onmicrosoft.com/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={id}
&client_secret={secret}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&grant_type=client_credentials

Getting 'access_denied' when trying to get oauth token for ejabberd, to call REST API

My main intention is to save users, shared_rosters, add users to shared_rosters etc... from my website. I am using NodeJS for the backend.
I couldnt really find any library to do the above mentioned tasks, hence I decided to use the REST API with JSON, available with ejabberd(please share any libraries that can do the above mentioned tasks). For this, I am trying to get an access token through oauth for ejabberd. I have followed the steps mentioned in the ejabberd docs.
I have added the following to my 'ejabberd.yml' file.
-
port: 5280
module: ejabberd_http
request_handlers:
"/websocket": ejabberd_http_ws
"/log": mod_log_http
# OAuth support:
"/oauth": ejabberd_oauth
# ReST API:
"/api": mod_http_api
web_admin: true
http_bind: true
captcha: true
Also added the oauth specific parameters in the yml file.
commands_admin_access: configure
commands:
- add_commands: user
oauth_expire: 3600
oauth_access: all
I am not sure if the placement of the oauth parameters is very important. I was not sure where to place it, so I placed it above the following line. I saw this placement on some other page.
### DATABASE SETUP
Restarted Ejabberd.
When i go to the following URL, I get the expected page
http://example.net:5280/oauth/authorization_token?response_type=token&client_id=Client1&redirect_uri=http://client.uri&scope=get_roster+sasl_auth
Now, when i put in my admin's username and password, as below
User: admin#xxx.xxx.x.xxx
Server: xxx.xxx.x.xxx
Password: admin
I get the response as
error=access_denied
I am not sure what I am missing. Can anyone please help me out here?
I was also doing the same thing, actually, when I entered data as
User: admin
Server: xxx.xxx.x.xxx
Password: admin
I was able to get the token.

Resources