How can I get userinfo from the bearer token of RedHat Openshift? - oauth-2.0

I have configured the OAuth client on RedHat OpenShift so that I can do SSO for my application using the inbuilt OAuth server of the RedHat OpenShift cluster. I got redirected to OCP login page, authenticated via OCP, and got the access_token as well. But now I want to get userinfo from the token I got. But it seems API /oauth/userinfo is not returning the user information.
Getting this error when I try GET /oauth/userinfo
REST API /oauth/userinfo
Am I missing something?

The /oauth/userinfo is apparently not there. You can get the user information using OpenShift API itself /apis/user.openshift.io/v1/users/{name}
The thing that I was missing is that a current logged in user is returned when you put ~ (tilde) to the path, so GET /apis/user.openshift.io/v1/users/~
{
kind: "User",
apiVersion: "user.openshift.io/v1",
metadata: {
name: "kube:admin",
selfLink: "/apis/user.openshift.io/v1/users/kube%3Aadmin",
creationTimestamp: null,
},
identities: null,
groups: [
"system:authenticated",
"system:cluster-admins",
],
}
As I'm using CodeReady containers for the development, I have URLs set up like this (Node.js/Next.js/next-auth):
export const OpenShiftOAuthProvider = {
id: "openshift",
name: "OpenShift",
type: "oauth",
version: "2.0",
params: { grant_type: "authorization_code" },
scope: "user:full",
idToken: false,
accessTokenUrl: "https://oauth-openshift.apps-crc.testing/oauth/token",
profileUrl: "https://api.crc.testing:6443/apis/user.openshift.io/v1/users/~",
authorizationUrl:
"https://oauth-openshift.apps-crc.testing/oauth/authorize?response_type=code",
clientId: "<yourclientid>",
clientSecret: "<yourclientsecret>"
async profile(profile) {
return {
id: profile.metadata.name,
name: profile.metadata.name
};
},
};

Related

Is there a way to get user name and email with the token returned by oauth2 in Foundry?

I am creating a company page (outside Foundry) which will use Foundry for authentication. I would like to show the name and email of the person who logged in in my website, is there a way to get it from the obtained token?
I checked the (internal) documentation pages for multipass and resource policy manager but could not see anything that would help.
You can call the user_info endpoint of multipass:
def get_user_info(self, hostname: str, auth_token: str) -> dict:
"""
Returns the multipass user info
Returns: dict, example:
{
'id': '1234bda5-686e-4fcb-ad52-d95e4281d99f',
'username': '<username>',
'attributes': {'multipass:email:primary': ['...'],...}
}
"""
response = requests.get(
f"{hostname}/multipass/api/me", headers={
"content-type": "application/json",
"authorization": f"Bearer {auth_token}",
}
)
response.raise_for_status()
return response.json()

oauth with next-auth ans Okta not working

I'm trying to provide SSO to a next.js app with Okta but it's not working and I don't know why. Worked perfect;y with Google.
I get the error: [next-auth][error][GET_AUTHORIZATION_URL_ERROR] https://next-auth.js.org/errors#get_authorization_url_error only valid absolute URLs can be requested
and stack output:
provider: {
id: 'okta',
name: 'Okta',
type: 'oauth',
wellKnown: 'xxxxxxx.okta.com/.well-known/openid-configuration',
authorization: { params: [Object] },
idToken: true,
profile: [Function: profile],
checks: [ 'state' ],
clientId: 'xxxxxxxxxx',
clientSecret: 'xxxxxxxxx',
issuer: 'xxxxx.okta.com',
signinUrl: 'http://localhost:3000/api/auth/signin/okta',
callbackUrl: 'http://localhost:3000/api/auth/callback/okta'
},
message: 'only valid absolute URLs can be requested'
It's frustrating because I have no idea what the issue is. The callback and signinURI look fine and match what I entereed in Okta web app setup.
In [...nextauth].js I have tried to enable debugging but that gave me zero extra output:
export default NextAuth({
providers: [
OktaProvider({
clientId: process.env.OKTA_CLIENT_ID,
clientSecret: process.env.OKTA_CLIENT_SECRET,
issuer: process.env.OKTA_DOMAIN,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
debug: true,
});
If anyone has any ideas I would be super grateful. It would be nice to at least know what next-auth is doing under the hood - like checking which url it is trying to reach etc.
Many thanks!
Tony
I just tried this today and indeed I got the same error -- my fix was to add https:// to the front of the issuer value and that seemed to make it work (which was interesting because the tutorial I was following specifically said to omit it).
eg instead of:
issuer: 'xxxxx.okta.com',
I used:
issuer: 'https://xxxxx.okta.com',
and that seemed to resolve my issue.
Hope this helps!
I put together a blog post for setting up Next.js, NextAuth.js, and Okta. It gives examples of your //pages/api/[...nextauth].ts file and .env.local as well. It shows using https for issuer variable as well.
https://thetombomb.com/posts/nextjs-nextauth-okta

How to obtain the Token endpoint from a NextAuth Provider configured with the wellKnown option?

NextAuth v4 implemented the possibility of configuring providers using the OIDC Discovery feature, that is, by defining just a wellKnown option (here) pointing to this discovery endpoint provided by the Authorization Service, e.g.:
export const oauthCustomProviderConfig = {
id: 'myprovider',
name: 'MyProvider',
type: 'oauth',
version: '2.0',
wellKnown:
process.env.A6_APP_OAUTH_PROVIDER_DISCOVERY_ENDPOINT ||
'http://localhost:9081/auth/realms/myrealm/.well-known/openid-configuration',
idToken: true,
issuer: process.env.A6_APP_OAUTH_PROVIDER_ISSUER || 'realms/myrealm/',
checks: ['pkce', 'state'],
async profile(profile, tokens) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
};
},
clientId: process.env.A6_APP_OAUTH_CLIENT_ID || 'clientId',
clientSecret: process.env.A6_APP_OAUTH_CLIENT_SECRET || 'clientSecret',
};
This works fine, but then in order to develop a "Refresh Token Rotation" solution, we have to manually call the Token endpoint of our provider. As per the linked documentation:
async function refreshAccessToken(token) {
try {
const url =
"https://oauth2.googleapis.com/token?" +
new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
})
...
I would expect to be able to access the configured Token endpoint using the getProviders() feature (here), however, this doesn't provide the configured endpoints:
{
myprovider: {
id: 'myprovider',
name: 'MyProvider',
type: 'oauth',
signinUrl: 'http://localhost:90/api/auth/signin/myprovider',
callbackUrl: 'http://localhost:90/api/auth/callback/myprovider'
}
}
I think the only solution at this point would be to manually provide the Token endpoint as a configuration in my application, or to explore the discovery endpoint manually (adding a request each time we refresh the token), which makes the use of the wellKnown feature not optimal.
Is there a way to obtain the configured Token endpoint from the configured provider?

How can I represent 'Authorization: Bearer <token>' in a Swagger Spec (swagger.json)

I am trying to convey that the authentication/security scheme requires setting a header as follows:
Authorization: Bearer <token>
This is what I have based on the swagger documentation:
securityDefinitions:
APIKey:
type: apiKey
name: Authorization
in: header
security:
- APIKey: []
Maybe this can help:
swagger: '2.0'
info:
version: 1.0.0
title: Bearer auth example
description: >
An example for how to use Bearer Auth with OpenAPI / Swagger 2.0.
host: basic-auth-server.herokuapp.com
schemes:
- http
- https
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
description: >-
Enter the token with the `Bearer: ` prefix, e.g. "Bearer abcde12345".
paths:
/:
get:
security:
- Bearer: []
responses:
'200':
description: 'Will send `Authenticated`'
'403':
description: 'You do not have necessary permissions for the resource'
You can copy&paste it to https://editor.swagger.io to check out the results.
There are also several examples in the Swagger Editor web with more complex security configurations which could help you.
Important: In this example, API consumers must include the "Bearer" prefix as part of the token value. For example, when using Swagger UI's "Authorize" dialog, you need to enter Bearer your_token instead of just your_token.
Bearer authentication in OpenAPI 3.0.0
OpenAPI 3.0 now supports Bearer/JWT authentication natively. It's defined like this:
openapi: 3.0.0
...
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT # optional, for documentation purposes only
security:
- bearerAuth: []
This is supported in Swagger UI 3.4.0+ and Swagger Editor 3.1.12+ (again, for OpenAPI 3.0 specs only!).
UI will display the "Authorize" button, which you can click and enter the bearer token (just the token itself, without the "Bearer " prefix). After that, "try it out" requests will be sent with the Authorization: Bearer xxxxxx header.
Adding Authorization header programmatically (Swagger UI 3.x)
If you use Swagger UI and, for some reason, need to add the Authorization header programmatically instead of having the users click "Authorize" and enter the token, you can use the requestInterceptor. This solution is for Swagger UI 3.x; UI 2.x used a different technique.
// index.html
const ui = SwaggerUIBundle({
url: "http://your.server.com/swagger.json",
...
requestInterceptor: (req) => {
req.headers.Authorization = "Bearer xxxxxxx"
return req
}
})
Posting 2023 answer in JSON using openapi 3.0.0:
{
"openapi": "3.0.0",
...
"servers": [
{
"url": "/"
}
],
...
"paths": {
"/skills": {
"put": {
"security": [
{
"bearerAuth": []
}
],
...
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}
Why "Accepted Answer" works... but it wasn't enough for me
This works in the specification. At least swagger-tools (version 0.10.1) validates it as a valid.
But if you are using other tools like swagger-codegen (version 2.1.6) you will find some difficulties, even if the client generated contains the Authentication definition, like this:
this.authentications = {
'Bearer': {type: 'apiKey', 'in': 'header', name: 'Authorization'}
};
There is no way to pass the token into the header before method(endpoint) is called. Look into this function signature:
this.rootGet = function(callback) { ... }
This means that, I only pass the callback (in other cases query parameters, etc) without a token, which leads to a incorrect build of the request to server.
My alternative
Unfortunately, it's not "pretty" but it works until I get JWT Tokens support on Swagger.
Note: which is being discussed in
security: add support for Authorization header with Bearer
authentication scheme #583
Extensibility of security
definitions? #460
So, it's handle authentication like a standard header. On path object append an header paremeter:
swagger: '2.0'
info:
version: 1.0.0
title: Based on "Basic Auth Example"
description: >
An example for how to use Auth with Swagger.
host: localhost
schemes:
- http
- https
paths:
/:
get:
parameters:
-
name: authorization
in: header
type: string
required: true
responses:
'200':
description: 'Will send `Authenticated`'
'403':
description: 'You do not have necessary permissions for the resource'
This will generate a client with a new parameter on method signature:
this.rootGet = function(authorization, callback) {
// ...
var headerParams = {
'authorization': authorization
};
// ...
}
To use this method in the right way, just pass the "full string"
// 'token' and 'cb' comes from elsewhere
var header = 'Bearer ' + token;
sdk.rootGet(header, cb);
And works.
By using requestInterceptor, it worked for me:
const ui = SwaggerUIBundle({
...
requestInterceptor: (req) => {
req.headers.Authorization = "Bearer " + req.headers.Authorization;
return req;
},
...
});
My Hackie way to solve this was by modifying the swagger.go file in the echo-swagger package in my case:
At the bottom of the file update the window.onload function to include a requestInterceptor which correctly formats the token.
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "{{.URL}}",
dom_id: '#swagger-ui',
validatorUrl: null,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
,
layout: "StandaloneLayout",
requestInterceptor: (req) => {
req.headers.Authorization = "Bearer " + req.headers.Authorization
return req
}
})
window.ui = ui
}
Solving this from laravel 7x ("openapi": "3.0.0"), edit your config\l5-swagger.php with the following codes
'securityDefinitions' => [
'securitySchemes' => [
'bearerAuth' => [
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT',
],
],
then you can add this as a security annotation for your endpoint:
*security={
*{
*"bearerAuth": {}},
*},

oauth2orize sample returns AuthorizationError: not authorized

I've downloaded the oauth2orize provider code example and the sample oauth client suggested in the docs and I'm getting the following error:
500 AuthorizationError: not authorized at validated
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/middleware/authorization.js:131:36)
at
/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/oauth2.js:180:14
at Object.exports.findByClientId
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/db/clients.js:24:10)
at exports.authorization.res.render.transactionID
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/oauth2.js:174:16)
at
/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/middleware/authorization.js:167:9 at pass
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/server.js:262:26)
at pass
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/server.js:280:9)
at pass
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/server.js:271:11)
at Server._parse
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/server.js:285:5)
at authorization
(/Users/trevorallred/projects/pics/node-soa/oauth2orize/examples/all-grants/node_modules/oauth2orize/lib/middleware/authorization.js:118:12)
I'm running the client here: http://localhost:3002/
I click the link to "Connect with example-oauth2orize"
It redirects me to the provider: http://localhost:3000/login
After logging in with "bob" I'm sent to this error page.
The only modifications I've made are to oauth-config.js in the example client.
'use strict';
module.exports = {
provider: {
protocol: "http",
host: "localhost:3000",
profileUrl: "/api/userinfo"
},
consumer: {
protocol: "http",
host: "localhost:3002"
}
};
I tried modifying oauth-consumer-config.js after I saw this bug report but it didn't seem to change any behavior.
module.exports = {
name: 'Example Consumer App'
, icon: 'http://example.com/icon_64.png'
, clientId: 'trevorclient'
, clientSecret: 'kasdfasdfoq34t134tg109gqerg1gjc'
};
Try updating your oauth-consumer-config.js file to this:
module.exports = {
name: 'Example Consumer App'
, icon: 'http://localhost:3000/icon_64.png'
, clientId: 'abc123'
, clientSecret: 'ssh-secret'
};
The problem is that the all-grants example doesn't actually use a DB, but a flat file for illustration purposes. If you navigation to db/clients.js in the all-grants project, you'll see the clients listed there that are allowed to connect via OAuth.

Resources