Error when using Okta Authentication with Next Auth - next.js

I am running into an error when using next-auth, and Okta as the provider. It redirects me back to my app but I get a page saying 'try signing in with another account' and redirects to 'api/auth/signin?error=Callback'
The error I get in the terminal with next auth's debugger is:
[next-auth][debug][oauth_callback_protection] Comparing received and expected state {
state: 'b3ef7bf3d4a5aa8f5f81fc95260502b0a206180bd0a831bb27b26d8c21271e33',
expectedState: 'b3ef7bf3d4a5aa8f5f81fc95260502b0a206180bd0a831bb27b26d8c21271e33'
}
[next-auth][error][oauth_get_access_token_error]
https://next-auth.js.org/errors#oauth_get_access_token_error {
statusCode: 401,
data: '{"errorCode":"invalid_client","errorSummary":"No client credentials found.","errorLink":"invalid_client","errorId":"******************","errorCauses":[]}'
} undefined undefined
[next-auth][error][oauth_get_access_token_error]
https://next-auth.js.org/errors#oauth_get_access_token_error {
statusCode: 401,
data: '{"errorCode":"invalid_client","errorSummary":"No client credentials found.","errorLink":"invalid_client","errorId":"**************","errorCauses":[]}'
} okta ************************
[next-auth][error][oauth_callback_error]
https://next-auth.js.org/errors#oauth_callback_error {
statusCode: 401,
data: '{"errorCode":"invalid_client","errorSummary":"No client credentials found.","errorLink":"invalid_client","errorId":"*******************","errorCauses":[]}'
}
This is my Okta App settings:
I have checked the cliendID and client secret and they are correct. Does anyone have any clues?
I know it can work with okta because I have had it working with another app, but I have tried to replicate the exact same setup.
in [...nextauth].js:
Providers.Okta({
clientId: process.env.OKTA_CLIENT_ID,
clientSecret: process.env.OKTA_CLIENT_SECRET,
domain: process.env.OKTA_DOMAIN,
accessTokenUrl: `https://${process.env.OKTA_DOMAIN}/oauth2/default/v1/token`,
authorizationUrl: `https://${process.env.OKTA_DOMAIN}/oauth2/default/v1/authorize/?response_type=code`,
})

It is a bug in next-auth package. Had the same issue and after debugging with a colleague and trying out a couple of things we came to following patch that works for us.
diff --git a/node_modules/next-auth/dist/server/lib/oauth/client.js b/node_modules/next-auth/dist/server/lib/oauth/client.js
index b4e48c2..7f68dd7 100644
--- a/node_modules/next-auth/dist/server/lib/oauth/client.js
+++ b/node_modules/next-auth/dist/server/lib/oauth/client.js
## -160,7 +160,7 ## function _getOAuth2AccessToken() {
headers.Authorization = 'Basic ' + Buffer.from(provider.clientId + ':' + provider.clientSecret).toString('base64');
}
- if ((provider.id === 'okta' || provider.id === 'identity-server4') && !headers.Authorization) {
+ if ((provider.id === 'identity-server4') && !headers.Authorization) {
headers.Authorization = "Bearer ".concat(code);
}
The issue seems to be an issue with sending both client_id and secret as query parameters but also sending a Authorization header. Removing the Authorization header made the idp integration work.
Also worth noting this; https://developer.okta.com/docs/reference/api/oidc/#client-secret

Related

Firebase Realtime Database returns 401 when trying to authenticate?

I am using REST API in my app to communicate with a Firebase RTDB, and trying to use a Google Access Token to authenticate my requests.
My issue is that with even the most permissive Rules on the database, I get HTTP error 401 in response to queries that try to authenticate.
For example, say I try to put some data in my database with the following command, I get 401 in return (all the values within < > are placeholders):
curl -XPUT -d '{ "UserID" : "<GOOGLE_UID>", "UserName" : "Clicksurfer", "CompletionMoves" : 8, "CompletionTime" : 16.21979 }' https://<FIREBASE_URL>.firebaseio.com/Level2/<GOOGLE_UID>.json/?access_token=<GOOGLE_ACCESS_TOKEN>
401
The strangest part is, when I abandon the use of access token altogether the query works:
curl -XPUT -d '{ "UserID" : "<GOOGLE_UID>", "UserName" : "Clicksurfer", "CompletionMoves" : 8, "CompletionTime" : 16.21979 }' https://<FIREBASE_URL>.firebaseio.com/Level2/<GOOGLE_UID>.json
200
As I said, I am currently using the most permissive rules for debugging:
{
"rules": {
".read": true,
".write": true
}
}
Any idea what might be causing this? Thanks in advance
EDIT:
I use the Google Play Games plugin for Unity in my project, among other things to get the AuthCode.
In order to do this, I needed to do a couple of things:
When building the config for Google Play Games during startup, I made sure to call the RequestServerAuthCode(false) method
Have the user login after Google Play Games sets up
Make sure that the relevant ClientID was supplied to Unity (in this case, it is a web client that has auth permissions on my Firebase rtdb).
This all looks like this:
public class GPGSAuthentication : MonoBehaviour
{
public static PlayGamesPlatform platform;
void Start()
{
if (platform == null)
{
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().RequestServerAuthCode(false).Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.DebugLogEnabled = true;
platform = PlayGamesPlatform.Activate();
}
Social.Active.localUser.Authenticate(success =>
{
if (success)
{
Debug.Log("GSPS - Logged in successfully");
}
else
{
Debug.Log("GSPS - Falied to login");
}
});
}
}
Now that we've done this, we can call PlayGamesPlatform.Instance.GetServerAuthCode() in order to get the AuthCode.
I traded in my AuthCode for an Access Token by sending a POST request to https://www.googleapis.com/oauth2/v4/token. In my query, I supply 4 fields:
client_id, which has the ID of the previously used client (where we got the AuthCode from).
client_secret, which has the correlating secret.
grant_type, which is always with the value "authorization_code"
code, which has the value of the AuthCode we got.
In response, I get a 200 response with 4 parameters:
access_token, the token I (fail to) use when authenticating against my Firebase rtdb.
token_type, the type of the aforementioned token.
expires_in, the amount of time before the token expires (I presume in seconds unit)
refresh_token, a token which can be used in order to get a new access_token without having to keep the Google user connected.
I then supply this access_token value to the queries I send to my DB, and promptly get the 401 error.

Access token request results in 302 in Angular HttpClient

I'm trying to authenticate requests for WordPress rest-api using grant type password. OAuth2 authentication in WordPress is provided by WP OAuth Server plugin.
When I request access token using Postman Chrome app the server responds with expected access token object but the similar request doesn't work in Angular. It gives status 302 and due to xhr redirect to login page, I'm not able to get access token object. I'm using Angular 5.
Here's how I request access token in Angular:
/* Example token url
AuthProvider.TOKEN_URL:
https://www.example-wordpress.com/oauth/token
*/
const body = {
grant_type: 'password',
username: username,
password: password,
};
const headers = new HttpHeaders()
.set('Authorization', 'Basic ' + btoa(AuthProvider.CLIENT_ID + ':' + AuthProvider.CLIENT_SECRET));
this.http.post(AuthProvider.TOKEN_URL, body, { headers: headers });
The above request produces 302 with location header set to:
https://www.example-wordpress.com/login/?redirect_to=https%3A%2F%2Fwww.example-wordpress.com%2Foauth%2Ftoken
And then a xhr GET request is made to above location which responds with HTML of login page and hence no access token is obtained.
The similar POST request for access token in Postman works fine and results in expected access token object but I can't get it to work in Angular.
EDIT
While debugging I generated JavaScript code for access token request from Postman and pasted in console of Chrome after importing jQuery.
The request works as expected in console as well and no redirection occurs. The response is JSON with access token.
Here's the code Postman generated for the POST request:
var settings = {
"async": true,
"crossDomain": true,
"url": "https://example-wordpress.com/oauth/token",
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded",
"authorization": "Basic M0wzakE3d080VmxxbXB0UUF1dUI5RkxicWxmeE8yR25Zdk4xQmxvbTp4TktTYnJ1Mno5cEp2VDFMbTNGNFhEQm10eDZzUGsya1FqZDg3VmQ2",
"cache-control": "no-cache",
"postman-token": "46339abe-2d1a-1032-f5d8-36e3193d9a81"
},
"data": {
"grant_type": "password",
"username": "my-username",
"password": "my-password",
"client_id": "3L3jA7wO4VlqmptQAuuB9FLbqlfxO2GnYvN1Blom",
"client_secret": "xNKSbru2z9pJvT1Lm3F4XDBmtx6sPk2kQjd87Vd6"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
And here's the response logged from above code:
{
access_token: "rksen3p351fj0povsrpfv2eeuahrciglc3ilphhy",
expires_in: 3600,
token_type: "Bearer",
scope: "basic",
refresh_token: "fudju8tecbnwly2e1xgfv92tykvpsniwkfpvrd7d"
}
I'm unable to figure out why redirection occurs when we request through Angular and not responds with access token JSON.
Any help is appreciated.
access_token (which I imagine is what you expect to have) isn't part of the few headers that Angular is able to read without setting up your server.
Angular only read "basic" headers such as Content-type. This is because of the default CORS configuration that only reads Cache-Control, Content-Language, Content-Type, Expires, Last-Modified and Pragma. When it comes to custom headers, you have to tell your server to expose the headers.
This is done through the Access-Control-Expose-Headers header.
There was no problem at all. It was a very very silly mistake. I apologize.
I was testing with two websites simultaneously and both had similar configuration. The only difference was that one had OAuth plugin installed and other not. So when I tried to authorize the request from Angular with the website which hadn't had OAuth2 plugin installed and so redirected to the login page. The constant set for the AuthProvider.TOKEN_URL was incorrectly set, while when I was testing with other tools I was using correct url.
Anyway, this was all my mistake. It happens sometimes, when you don't take break. :)

Requesting for token and claims from on premises ADFS across domain

First I am new to the topic ADFS. The goal is a Single-Sign-On OAuth2 authentication for SPA Web application.
We have two on premises ADFS servers(called Server-A and Server-B) which is running on two different domain respectively(A, B).
Now users can be from any of the domain(A, B) and they can login into my application.
These two ADFS servers trust each other and the ADFS of Server-B has been added on ADFS of Server-A as Claim provider trust and ADFS of Server-A has been added on ADFS of Server-B as Relying party trust.
My application endpoint is ADFS of Server-A and my application hosted on IIS which is inside Server-A.
Now when any user of domain A trying to login in my application that time I am getting "access_token" and "Claims" both.
PROBLEM: When any user of domain B trying to login that time getting
below error message -
Error message while user of domain B trying to login.
Basically in this image step #7 getting failed.
I am using below javascript code for getting code and token
Below code to get the "Authorization code"
function getAuthorizationCode() {
var _url = _adfs_server + "/adfs/oauth2/authorize?response_type=code&client_id=" + _client_id + "&redirect_uri=" + _redirect_url + "&resource=" + _resource;
window.location.href = _url;
}
Above code snippet - I am getting the "Authorization code" from "Redirected URL" query string. And after that I am calling below code to get the access token
function getAccessToken(code) {
var redirect = decodeURIComponent(_redirect_url);
var _data = {
'grant_type': 'authorization_code',
'client_id': _client_id,
'redirect_uri': redirect,
'code': code
}
$.ajax({
type: "POST",
url: _adfs_server + "/adfs/oauth2/token",
crossDomain: true,
data: _data,
success: function (response) {
_token = 'Bearer ' + response.access_token;
callWebAPI();
}
});
}
So any configuration between two ADFS servers are missing or some other steps need to do?
What version of ADFS are you using?
You talk about OAuth but that's only available on Server 2012 R2 (ADFS v3.0). And even then, it has limited functionality.
The CP / RP trusts you mention above are for WS-fed. They are N/A for OAuth.
Also OAuth uses a JSON token not a SAML one.

What is the parameter name for basic authentication in meteor's http.post API?

I'm using HTTP.post in meteor and I need to send basic authentication with only a username to an external service. Where does this go and what would that look like?
I am only using it on the server side so I know it should look like the below code, but I'm not sure where to put the username and what to call it.
I've tried this.
var resultSet = HTTP.post("https://billy.balancedpayments.com/v1/customers", {
params: {"processor_uri": "/customers/customerURI"},
authentication: {"MYKEYHERE":""}
});
And this.
var resultSet = HTTP.post("https://billy.balancedpayments.com/v1/customers", {
params: {"authentication": "MYKEYHERE",
"processor_uri": "/customers/customerURI"}
});
And this.
var resultSet = HTTP.post("https://billy.balancedpayments.com/v1/customers", {
params: {"processor_uri": "/customers/customerURI"
},
headers: {'Authorization': 'MYKEYHERE'}
});
I get this error each time.
Error: failed [403] 403 Forbidden Access was denied to this resource.
Unauthorized: CustomerIndexView failed permission check
The plain auth : 'username:password' should do (from docs):
var resultSet = HTTP.post("https://billy.balancedpayments.com/v1/customers", {
params: {"processor_uri": "/customers/customerURI"},
auth: 'yourkey:'
});
As per the balanced payments documentation:
To authenticate with Balanced, you will need the API key secret provided from the dashboard. You have to use http basic access authentication. Your key has to be set as the username. A password is not required for simplicity.
So this means you leave the password blank, so its just your key followed by the colon :
Also you might want to consider using the balanced package for Meteor which does all the boilerplate for you.

Apigee 401 Unauthorized with token_expired

I'm trying to create a user account through the apigee JS API. This worked just fine when I was last doing this before the holidays in mid December. Now, however, I get a 401 Unauthorized error reading token_expired.
Is there a way to refresh the token? I don't know why it would have expired.
This is what I'm trying. First I instantiate the data client. No problems here:
var dataClient;
var client_creds = {
orgName: '*******',
appName: '*******'
}
dataClient = new Apigee.Client(client_creds);
Later, when trying to create a new user, I get the token_expired error:
dataClient.request(options, function (error, response) {
if (error) {
console.log(response);
alert("Something went wrong when trying to create the user. " + response.error)
// Error
} else {
// Success - the user has been created, now login.
dataClient.login(user.email, user.password,
function (err) {
if (err) {
//error - could not log user in
console.log("There was an error logging in " + user.name);
} else {
//success - user has been logged in
}
}
);
}
});
I've also tried dataClient.signup, but same error.
There are no refresh tokens within App Services; you'll need to follow the login flow in order to retrieve a new token. Note that you can specify the ttl parameter, like so, so you don't need to do this as frequently:
https://api.usergrid.com/{org}/{app}/token?ttl=604800
By default, this is set to 7 days, but you can change the default app max ttl to 0 (non-expiring) or something else like 31104000000 (365 days).
To do that, you make a PUT request:
https://api.usergrid.com/{org}/{app}/?client_id={app_client_id}&client_secret={app_client_secret}
With JSON payload:
{
"accesstokenttl":0
}
Or for 1 year:
{
"accesstokenttl":31104000000
}
If that doesn't work for you, the authorization tokens for the JavaScript SDK are kept in your browser's local storage. In Chrome, use the Developer Tools. In the Resources tab on the left hand side expand the Local Storage entry. You should see something like "http://usergrid.dev" or something similar. Choose that and on the right hand side you should see an entry for accessToken. Delete that and it should solve your problem.

Resources