Okta JWT Verifier is not working - javascript

I want to verify the OKTA Access Token.
I created a simple use-case in nodejs, but I get permission error and I didn't find any solution to resolve this issue.
Here is my code:
const OktaJwtVerifier = require('#okta/jwt-verifier');
const issuer = '{{url}}';
const cid = '{{clientid}}'
const oktaJwtVerifier = new OktaJwtVerifier({
issuer: `http://${issuer}.com/oauth2/default`,
assertClaims: {
cid
}
})
oktaJwtVerifier.verifyAccessToken("accesstoken")
.then(jwt => {
// the token is valid
console.log(jwt.claims);
})
.catch(err => {
console.log(JSON.stingify(err))
});
And I get this error:
name: 'JwksError',
message:
{ errorCode: 'E0000006',
errorSummary: 'You do not have permission to perform the requested action',
errorLink: 'E0000006',
errorId: 'oaezeHaXzOCS9m4D5C5P9WnZA',
errorCauses: [] } } }
I checked the jwt-verifier github page and I think it should work.

The "default" authorization server also need API Access Management feature flag enabled. Do you have API Access management?
to check:
Go to Okta dashboard > security > API > check if you have tab named "Authorization servers" beside the "token" tab.
If you don't:
If your org. is a preview org. email developers#okta.com with your org. URL and they will enable it
If your org is not preview, you have to buy it.
alternatively, you can use Okta itself as an authorization server -> so the issuer would be "issuer: http://${issuer}.com" not "issuer: http://${issuer}.com/oauth2/default"
If last one is the case:
you CAN NOT validate the token locally.
However, you can send the token to /introspect endpoint to be validated OR request more information with the token from /userinfo
You can get more info. about above from developers.okta.com

Related

How to get the Android or iOS App Origin Headers?

I created an api for my all my users to be able to retrieve with corresponding data. I'd restriced the api endpoints with apiKey, and origin from the headers.
Only allow to consume my api data by checking these specific origin and api_key from the headers.
I have no idea how to I get to restric for the mobile app. Some of my users are using my api with by building their own api. I do want to rectrict them to consume my api's data by restricting origin just like I am doing for the website.
The main question is "How do I get the specific app of their orign address just like we are dealing with for the web frontend browser?"
These are the sample to protect my own api endpoint which I'd written in NextJS.
// Check headers username and api_key
const username = req.headers.username
const api_key = req.headers['api_key']
// Only allow website with orign name can retrieve data
const origin = req.headers.origin
// if the client is using Mobile Device then restrict the access
// I could only check who are retrieve my api from their end by using user-agent
if (req.headers['user-agent'].includes('Mobile')) {
res.status(403).json({
status: 'error',
message: 'Access denied',
})
}
const users = subscribeAPIUsers.find(
(user) =>
user.username === username &&
user.api_key === api_key &&
origin === user.host
)
// If user is not found
if (!users) {
res.status(401).json({
status: 'error',
message: 'Unauthorized',
})
}
Any suggestions would be appreciate it. :)

How can I authenticate to request Firebase REST api to fetch the list of firebase projects of a signed-in user?

I wish to fetch the list of firebase projects of a signed-in user (not me) on my website.
I can't seem to request the firebase rest api correctly (endpoint) as I receive a 401 UNAUTHENTICATED error.
The steps I follow:
I am using firebase.auth().signInWithPopup on my browser to authenticate users. Here are the scopes used:
// Google provider
const provider = new GoogleAuthProvider();
// Additionnal scopes
provider.addScope("https://www.googleapis.com/auth/cloud-platform");
provider.addScope("https://www.googleapis.com/auth/firebase");
provider.addScope("https://www.googleapis.com/auth/datastore");
// Signin
signInWithPopup(auth, provider);
I am then using getIdToken method to get a token from the user.
const token = await auth.currentUser.getIdToken(true);
I use this token to request https://firebase.googleapis.com/v1beta1/projects:
const data = await window.fetch("https://firebase.googleapis.com/v1beta1/projects", {
headers: {
authorization: `Bearer ${token}`,
},
})
.then(res => res.json());
but I am always getting a 401 error:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
What am I doing wrong?
To get a list of Firebase projects for a user you need an OAuth2 token for a user that is a collaborator on the projects. You are trying to use the ID token/JWT for a Firebase Authentication user, which is not the same.
Follow the documentation on generating an access token to get value that you can use to get a list of projects.
I just found the answer, the firebase SDK does give you an accessToken with the idToken. You can then use this token instead of the idToken for any of your REST api needs.
const result = await signInWithPopup(auth, provider);
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);

Amplify (JavaScript) - SAML login

I'm struggling for a few days now with AD SAML login in Amplify.
I've seen a few tutorials but my desired flow is different.
I don't have any button which will user click to trigger Auth sequence (and open popup).
Our users are going into a link:
https://....amazoncognito.com/login?response_type=token&client_id=...&redirect_uri=...
Which automatically redirect them into the front-end.
http://localhost:3000/#access_token=...&id_token=...&token_type=Bearer&expires_in=600
Which I can grab the access_token, id_token, token_type and expires_in from the query params.
I can do calls to our back-end with those tokens succesfully..
But what I am struggling with is make Amplify "login" sequence.. so it will identify the user and will create the localStorage values.. so for example when calling Auth.currentSession() it will return the token.
I've tried using Auth.federatedSignIn(..) but to no avail. I am not even sure if it's support this flow or not.
This is what I tried:
Auth.federatedSignIn(
'test-ad',
{
token: idToken,
expires_at: expiresIn,
},
{
name: "test-user",
},
);
But I keep getting
Unhandled Rejection (NotAuthorizedException): Invalid login token. Issuer doesn't match providerName
Which is strange as the providerName I'm passing ('test-ad') is the same as I configured in Cognito SAML Provider Name field (in Cognito -> User Pool -> Federation -> Identity Providers -> SAML -> new Provider)
I have my Amplify configured correctly
Amplify.configure({
Auth: {
identityPoolId: ...,
region: ...,
userPoolId: ...,
userPoolWebClientId: ...
}
});
And I can see the Chrome network call is with the correct payload:
{
"IdentityPoolId":"us-east-x:...",
"Logins":{
"test-ad":"...idToken..."
}
}
so - How can I authenticate into Amplify using token? is it even possible?
Any help will be appreciated!

How do I make authenticated requests to Amplify GraphQL using Firebase Auth as OIDC?

I need help setting up Firebase Auth + Amplify GraphQL. I'm trying to log in using federated sign with securetoken.google.com/PROJECT-ID as the provider, and it seems to log in alright because when I call Auth.currentAuthenticatedUser() I get the token, and when listening to Hub "signIn" event I get the token. My problem is making authenticated requests to my GraphQL API.
const signIn = async () => {
try {
// already logged in using firebase so I just need to get the token from the current user
const tokenResult = await currentUser?.getIdTokenResult()
await Auth.federatedSignIn('securetoken.google.com/PROJECT-ID', {
token: tokenResult?.token,
})
const res = await Auth.currentAuthenticatedUser()
console.log('token', res.token) // eyjhxxxxxxxxxx...
} catch (error) {
// ...
}
}
const client = new AWSAppSyncClient({
url: AppSyncConfig.aws_appsync_graphqlEndpoint,
region: AppSyncConfig.aws_appsync_region,
auth: {
type: AppSyncConfig.aws_appsync_authenticationType,
jwtToken: () => getToken(),
},
})
const getToken = async () => {
const token = await Cache.getItem('#accessToken')
return token
}
When calling Auth.currentSession() I get "No current user". Also, I do see the token in the Authorization header when I attempt to fetch data.
I have had a similar issue so here are some things you can have a look at.
In the Appsync in the AWS Console
https://eu-west-1.console.aws.amazon.com/appsync/home
Make sure that your primary authorization mode is set to Open Id Connect, or add another authorization provider specifying "OpenId Connect" if you are happy with the primary.
If that does not solve it, you can try to add the #aws_oidc AppSync directive to your GraphQL schema.
type Query {
getPosts:[Post!]! #aws_oidc
}
or
type Post
#model
#auth(
rules: [
{ allow: owner, provider: oidc }
...
more here: https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/
Lastly, if you have more than one authorization provider, you might have to switch the primary authorization provider to "OpenId Connect" - the issue I had was that Cognito (primary) blocked my secondary API Key authorization provider.
Update
AWS uses IAM roles for everything related to security. So when you authenticate with whichever authentication provider an IAM role will be assigned to that request, and that IAM role needs permission on the resource in question, like execute permission on GraphQL queries, scanning of DynamoDB tables etc. as per this image:
So you might need specific rules set in the IAM console for the IAM role in question - or at least check that it has permission - if not, you'll also get an unauthorized error message in the Appsync GraphQL query console.
more here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WIF.html?icmpid=docs_ddb_console
and here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html?icmpid=docs_ddb_console
Try removing the cookie storage configuration in aws-exports.js may solve it. Maybe this helps you.
More discussion here Link-1 and Link-2

Get Auth0 access_token from existing id_token

I'm using auth0 to authenticate my logins to my Single Page App (built on React). I'm mostly using the base API calls (listed here).
The process I'm using is:
get username/email and password when the user enters them on my app's login page
Send a POST request to /oauth/ro with those values - here is that code:
export const login = (params, err) => {
if (err) return err
const {email, password} = params
const {AUTH0_CLIENT_ID, AUTH0_DOMAIN} = process.env
return fetch(`${AUTH0_DOMAIN}/oauth/ro`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'client_id': AUTH0_CLIENT_ID,
'username': email,
'password': password,
'connection': 'Username-Password-Authentication',
'grant_type': 'password',
'scope': 'openid',
'device': '',
'id_token': ''
})
})
.then(response => response.json())
.then(json => {
const {id_token, access_token} = json
setCookieValue('id_token', id_token) // utility function I wrote
return getProfile(access_token)
.then(data => {
const {user_id, email: emailAddress, picture, name} = data
return {id_token, user_id, emailAddress, picture, name}
})
})
.catch(error => console.log(`ERROR: ${error}`))
}
This is all sent through Redux and the user is logged in (assuming the username/password was correct).
However, I'm trying to figure out how to persist the login when refreshing the page/coming back to the app. I'm saving the id_token (which is a JWT) in the browser's cookies and can fetch this when the app renders server-side. I can decode the JWT and get the payload (sub is the user ID from auth0). However, to get the profile data I need the access_token which Auth0 provides when using the /oauth/ro POST request. Obviously, if the JWT token has expired then it will just reject it and keep the user logged out.
Here is my code to decode the JWT (happens on app render):
const ID_TOKEN = req.cookies.id_token || false
if (ID_TOKEN) {
verifyJwt(ID_TOKEN, (err, decoded) => {
if (err) { console.log(`JWT Verification error: ${err}`) }
else {
const {sub} = decoded
getProfile(sub).then(data => store.dispatch(fetchUserDetails(data))) // fails as `sub` (the user id) is not the `access_token` which it requires
}
})
}
I have tried using the /oauth/ro call again, but this time specifying "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer" and using the id_token retrieved from the cookies, and specifying a device. However, when I do this call, I get this error from Auth0:
{
"error": "invalid_request",
"error_description": "there is not an associated public key for specified client_id/user_id/device"
}
So my question is, what API call do I need to make to get the access_token from the id_token JWT?
Also, as a bonus - when I do the POST request to login, the password is being transfered over plaintext. How would I encrypt this when sending to auth0 so they can decrypt it back? I assume it involves using the client_secret which auth0 provide but I'm not sure how to go about doing that.
The ability to refresh a token programmatically without any type of user interaction is accomplished through the use of refresh tokens. However, this is not applicable for browser-based applications because refresh tokens are long-lived credentials and the storage characteristics for browsers would place them at a too bigger risk of being leaked.
If you want to continue to use the resource owner password credentials grant you can choose to ask the user to input the credentials again when the tokens expire. As an alternative, upon authentication you can obtain the required user information and initiate an application specific session. This could be achieved by having your server-side logic create an application specific session identifier or JWT.
You can also stop using the resource owner password credentials grant and redirect the user to an Auth0 authentication page that besides returning the tokens to your application would also maintain an authenticated session for the user, meaning that when the tokens expired and your application redirected again to Auth0, the user might not need to manual reenter credentials because the Auth0 session is still valid.
In relation to the password being sent in plaintext; the resource owner endpoint relies on HTTPS so the data is encrypted at the protocol level. You must also use HTTPS within your own application for any type of communication that includes user credentials of any kind.
Also note that you can control what's returned within the ID token through the use of scopes, depending on the amount of information in question you might not even need to make additional calls to get the user profiles if you signal that you want that information to be contained within the ID token itself.

Categories