Keycloak error : Code not valid - for client token request - vue.js

Few days Ago I have integrate keycloak with my php application.
Which working fine. Now I am trying to do same thing for my vue js app.
In 2nd step (for client token request using authorization code) I am getting 400 error.
Response Message "Code not valid".
1st step : (inside mounted )
const AUTHORIZE_URL = 'auth/realms/rstore/protocol/openid-connect/auth';
const params = {
'response_type': 'code',
'redirect_uri': 'http://localhost:8080/sso/callback',
'client_id': client_id,
'nonce': uuid(),
'state': uuid(),
'scope': 'openid profile email'
};
window.location = baseUrl + AUTHORIZE_URL + '?' + queryString.stringify(params);
2nd step : (For client token request)
let url = baseUrl + ACCESS_TOKEN_URL;
let params = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': 'http://localhost:8080/sso/callback',
'client_id': client_id,
'client_secret': client_secret
};
let result = fetch(url, {
method: 'POST',
body: queryString.stringify(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded' // ,
}
})
.then(resp => {
return resp.json();
})
.catch(error => {
throw new Error('FetchError in request to ES: ' + error.toString())
})
I also tried from command prompt --->
curl -X POST 'https://example.com/auth/realms/nstore/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=095516b7-e545-4b02-9dad-ec9c6366e0e4.33e1f298-a440-4bdc-9118-96ed669cabcd.e1c5d85f-3441-490d-a1fd-eb3b00d3c47c' \
--data-urlencode 'client_id=vue' \
--data-urlencode 'client_secret=b329ade3-2b71-4e3b-ab25-926cb32c5c8c' \
--data-urlencode 'redirect_uri=http://localhost:8080/sso/callback'
output same ---> {"error":"invalid_grant","error_description":"Code not valid"}

The "Code not valid" error message is a general one. It may have one of the following meanings:
http://localhost:8080/auth/realms/{realm_name}/protocol/openid-connect/auth
code is not valid, or is valid but incorrently URL encoded
the code is correct, but it has been already used by other user
session
Each authorization code can be used only once, to generate single new access token. As such, generating multiple access tokens from one code is not possible.
One reason why you may be receiving this error is because authorization code was already used, either by Postman or by web application.
Solution : regenerate the client_secret in the keycloak server for your realm and then do the complete process again and you will get the accesstoken and referesh token.
Note : Each authorization code can be used only once, to generate single new access token. As such, generating multiple access tokens from one code is not possible.

Related

UPS API OAuth token request fails

In the UPS developer portal, I have created an application that has a Client Id and a Client Secret. Next, I want to obtain an OAuth token so I can use it to access their other APIs. I am creating my token request as per the spec and I am receiving the following error:
{"response":{"errors":[{"code":"10400","message":"Invalid/Missing Authorization Header"}]}}
The spec has a "try it out" feature where you can obtain a test token. It prompts the user to fill in a x-merchant-id parameter and a grant_type form variable and creates a curl request that looks like this:
curl -X POST "https://wwwcie.ups.com/security/v1/oauth/token"
-H "accept: application/json"
-H "x-merchant-id: {My_Client_Id_Goes_Here}"
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=client_credentials"
For x-merchant_id, I have used my app’s Client Id. It is not clear if the value for grant_type should be the phrase client_credentials (the page makes it seem like this is the only valid value) or my app’s actual Client Secret. I have tried both and get the same error each time.
There are a million examples out there on how to use their (old style) API keys, but practically nothing about how to obtain an OAuth token except for the instructions linked above!
Your curl looks good to me, just missing the Authorization header which is a base64(id:secret)
curl -X POST "https://wwwcie.ups.com/security/v1/oauth/token"
-H "Authorization: Basic {id}:{secret}"
-H "accept: application/json"
-H "x-merchant-id: {My_Client_Id_Goes_Here}"
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=client_credentials"
If you're using the 'Try out' feature, select the Authorize button at the top and enter the client id and secret, that's where its used to set the Authorization header. One thing to note, the 'Try out' feature only work with the Test product(s) assigned to your app
Additional info
UPS have 2 environments
Testing: wwwcie.ups.com
Production: onlinetools.ups.com
Testing env only accepts Test Products, so note the product(s) that was added to your app
I was stuck with this issue for a long time.
Your comments did eventually help me. But I wanted to make it more clear for someone else reading this later....
Instead of using UPS username and password in the authorization header. You need to encode the clientId and secret with a colon between and send that.
For PHP:
$clientID = base64_encode("{clientID}:{clientSecret}");
$headers = array();
$headers[] = "Authorization: Basic $clientID";
$headers[] = 'Accept: application/json';
$headers[] = "X-Merchant-Id: {clientID}";
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
One more addition to the other answers: make sure you add the "OAuth" product to your UPS app. I had added "tracking" and "tracking test", but not OAuth. I was getting the "{"code":"10401","message":"ClientId is Invalid"}" response when I tried to get a token, even though I was sure I had everything else right.
Adding OAuth to my UPS app presumably added my ClientID to their OAuth system, and my token requests started working.
Just in case somebody with .NET/C# background will be looking for the similar topic - an UPS RESTFul API authorization and tracking info processing solution here is the one working well for me using proposed here approach:
#define TEST_MODE
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
var myClientID = "{Type your ClientId here}";
var mySecretID = "{Type your SecretID here}";
#if TEST_MODE
var baseAddress = "https://wwwcie.ups.com"; // testing
#else
var baseAddress = "https://onlinetools.ups.com"; // production
#endif
var accessID = $"{myClientID}:{mySecretID}";
var base64AccessID = Convert.ToBase64String(Encoding.ASCII.GetBytes(accessID));
using (var client = new HttpClient())
{
// Get Access Token
var request = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{baseAddress}/security/v1/oauth/token"),
Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials")
})
};
request.Headers.Add("Authorization", $"Basic {base64AccessID}");
var response = await client.SendAsync(request);
var jsonResult = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<JsonObject>(jsonResult);
var access_token = result?["access_token"]?.ToString();
// Get Tracking Info
var trackingNumber = "1Z5338FF0107231059"; // provided by UPS for testing
request = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri($"{baseAddress}/api/track/v1/details/{trackingNumber}")
};
request.Headers.Add("Authorization", $"Bearer {access_token}");
request.Headers.Add("transId", $"{DateTime.Now.Ticks}");
#if TEST_MODE
request.Headers.Add("transactionSrc", $"testing");
#else
request.Headers.Add("transactionSrc", $"{App Name and version}");
#endif
response = await client.SendAsync(request);
jsonResult = await response.Content.ReadAsStringAsync();
Console.WriteLine(jsonResult);
}

Unable to generate OAuth 2.0 Access Token from Office365 via JavaScript

I'm trying to pull an access token from Office365's /token identity platform endpoint via OAuth 2.0 client credentials grant flow. I have my app registered, the client ID & secret, etc...
I can make the POST request in Postman and receive the access token without issue:
However, when I try the POST request via JavaScript (by way of Google Apps Script), I receive an error message: AADSTS900144: The request body must contain the following parameter: 'grant_type'
I've already Google'd this error and found a bunch of different solutions, and have tried implementing them to no avail. I imagine this has to do with the URL encoding, but cannot figure it out.
Code:
function getO365() {
// POST Request (To get Access Token)
var tenantID = 'longstringhere'
var appID = 'longstringhere'
var appSecret = 'longstringhere'
var graphScore = 'https://graph.microsoft.com/.default'
var url = 'https://login.microsoftonline.com/' + tenantID + '/oauth2/v2.0/token'
var data = {
'client_id': appID,
'scope': graphScore,
'client_secret': appSecret,
'grant_type': 'client_credentials'
};
var postOptions = {
'method': 'POST',
'headers': {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
'body': data,
'redirect': 'follow'
};
var authToken = UrlFetchApp.fetch(url, postOptions);
}
The only real difference between my code and the JavaScript Fetch code I pulled off of Postman is:
var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "longstringhere");
urlencoded.append("scope", "https://graph.microsoft.com/.default");
urlencoded.append("client_secret", "longstringhere");
urlencoded.append("grant_type", "client_credentials");
When I try to use URLSearchParams in Google Apps Script, I keep getting this error: ReferenceError: URLSearchParams is not defined
Any ideas? Thanks in advance!
This was resolved by changing 'body' to 'payload' for UrlFetchApp per the documentation. Edited code to reflect the change. Credit to #TheMaster for pointing out my mistake.
'payload': data,//from 'body': data,

Identity Server 4

Beginner level query alert. IdentityServer4 Tutorial After going through the tutorials what I inferred was that-
I create an authorization server, whose job is to issue token for the client with proper authentication.
My Authorization Server runs first, and includes information and definitions of the API and client.
The API has an authentication middleware that validates the incoming token to make sure if its coming from a trusted source and also its scope.
The client requests a token from the authorization server and then sends request to the API with the token received.
For all this, I had to run the authorization server first, the API next and then the Client. My requirement is that I don't need a start and stop server which runs separately to take care of authentication. I have one API and I need it to double as the authorization server too. Is this possible? Is it possible for the API to generate tokens, validate them and then tend to the requests, all the while using IdentityServer4.
Update Jan 2020: For a ASP.NET Core 3.1 example of using IdentityServer4 in the same project as ASP.NET Core API controllers, you can have a look at my IdentityServer4 with MVC Controllers and AppInsights sample repo. It's goal was to test AppInsights, but it does demonstrate a SPA stub that calls both OpenID endpoints (⚠ in a non-recommended wa, using client credentials), and controller endpoints.
Although typically the Auth Server will be separate from the Resource Server, this doesn't need to be the case. You can just add all of it to one application. Here's an example.
Create a new ASP.NET Core (I used 2.0) Web API application.
Install-Package IdentityServer4 -Version 2.0.0-rc1 (at the time of writing rc1 is the version with .NET Core 2.x support)
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Set [Authorize] on ValuesController from the template
Add this code to Configure(...) in class Startup above app.UseMvc():
// calls app.UseAuthentication() for us
// See: http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
app.UseIdentityServer();
Add this code to ConfigureServices(...) in class Startup:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(new[]
{
new ApiResource
{
Name = "MyApi",
ApiSecrets = { new Secret("supersecret".Sha256()) },
Scopes = { new Scope("myapi") },
}
})
.AddInMemoryClients(new[]
{
new Client
{
ClientId = "api",
ClientSecrets = { new Secret("supersecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = { "myapi" },
}
})
.AddTestUsers(new List<TestUser>
{
new TestUser
{
SubjectId = "some-unique-id-12345678980",
Username = "john",
Password = "123456"
}
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.Authority = "http://localhost:51689";
opts.Audience = "MyApi";
opts.RequireHttpsMetadata = !env.IsDevelopment();
});
If you now F5 the app it will show an empty page because of a "401 Unauthorized" response. You can also now check this endpoint: http://localhost:51689/.well-known/openid-configuration (with your dev port of course).
You can also do this now:
curl -X POST \
http://localhost:51689/connect/token \
-H 'authorization: Basic YXBpY2xpZW50aWQ6c3VwZXJzZWNyZXQ=' \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=john&password=123456&grant_type=password'
Note that the authorization header contains a base64 encoded string representing the string "apiclientid:supersecret". This should give you a result like this:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODE3OTAsImV4cCI6MTUwNTA4NTM5MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODE3OTAsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.sxWodlJKDJgjoOj-8njZ8kONOqiKgj3E5YlKXGX5cz-WqUK7RHKJacNX09D00Y8YtmZpkc5OrY0xzOx7UuSAtDku4oOX_1o38XEGJPBSJHdjqgVGSOU-hwDkzin8HSRJ0Kna1vM3ZzTh80cFTVhP8h903GAPRrAyV8PtRXnwV0CPel8NdvML6dV-mfDpGi0l7crp-TPnH4nIG0olpRYUPV5EsgCVMG9vswnOnKz3RPOGaU8yJy7_9mbQW5GHKfN0J6swiSt5rY3NKs_t1P9-tnCDKBOAafaXjLEO3Kx4fP4xTgwK92uKcEDDnRZo_-T0CkBxnSQm0oz1sUyrW8_3Pg",
"expires_in": 3600,
"token_type": "Bearer"
}
In addition to the option of switching to other authentication flows, you can also add a controller method like this:
[Route("api/token")]
public class TokenController
{
[HttpPost("request")]
public async Task<JObject> Request(string username, string password)
{
var tokenClient = new TokenClient("http://localhost:51689/connect/token", "apiclientid", "supersecret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password);
if (tokenResponse.IsError) { /* Log failed login attempt! */ }
return tokenResponse.Json;
}
}
And then call it like this:
curl -X POST \
http://localhost:51689/api/token/request \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=john&password=123456'
This should give a similar response as above.
You can now provide this access_token insde a header Authorization: Bearer access_token_should_go_here like this:
curl -X GET \
http://localhost:51689/api/values \
-H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODIyODQsImV4cCI6MTUwNTA4NTg4NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODIyODQsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.hQ60zzEbZOSVpP54yGAnnzfVEks18YXn3gU2wfFgNB33UxQabk1l3xkaeUPTpuFdmFTm4TbVatPaziGqaxjzYgfdVoAwQ3rYJMuYzOh0kUowKxXTkquAlD13ScpvxrGeCXGxFTRHrxX2h-1hHGQ9j2y2f3-ESynzrCdxp5HEH1271BSYfQ7pZIzvyxxpbmOzzKDzdYfcJV6ocnOU4jXBhw6iOzqpR03zxxtjIjGbJd2QwWklBGqZlO_thdZZFi-t7zu5eC4wqRCYGGZYWOUC17_Btc_Irg2SsvLCUDzsaBw7AVgLpZ7YjF-RsVqIi6oxNQ2K0zllzUy8VbupbWKr5Q' \
-H 'cache-control: no-cache' \
And now you should get past the [Authorize] atribute. Yay!
You now have one web application, which acts as both an Auth Server and a Resource Server.
Fun fact: with the above example the AddJwtBearer options specify the application's own url as an Authority, making the app request from itself the public key to use for validating the tokens. You could instead also use code to directly provide this key to the authentication middleware.

Using Taxee.io API

I'm trying to access the Taxee.io API using the request npm module. The documentation is slightly poor and the difference between the Mashape info and the website's info is confusing.
https://taxee.io/
The docs have one example of a request here.
curl 'https://taxee.io/api/v2/calculate/2017' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJBUElfS0VZX01BTkFHRVIiLCJodHRwOi8vdGF4ZWUuaW8vdXNlcl9pZCI6IjU4NDQ4MTA4Mzg2NjhhMTU4ZDU0ZmIzNSIsImh0dHA6Ly90YXhlZS5pby9zY29wZXMiOlsiYXBpIl0sImlhdCI6MTQ5OTA1MzU0NX0.pOwC5JEC7trLaaZVgHHGu_rvN0-EGa3RMm8BgJ-M9gk' -H 'Content-Type: application/x-www-form-urlencoded' --data 'state=NC&filing_status=married&pay_periods=26&pay_rate=116500&exemptions=2'
I however want to use the request npm module and am struggling to bridge the gap in how it will work in my express app.
const request = require('request');
request.post('https://taxee.io/api/v2/calculate/2017', {
'auth': {
'Bearer': 'mykey'
}
});
This is what I have thus far. Any help is appreciated.
Keep in mind that properties are case sensitive in JavaScript. You must pass the bearer token under the key bearer and not Bearer.
To replicate the Content-type and pass data, use the form support of the library.
E.g. like this:
{
auth: {
bearer: '<token>',
},
form: {
state: 'NC',
// ...
},
}

LexikJWTAuthenticationBundle with ionic app

I have seen many post on this subject, but i can't make a correct request, regardless all of my tests.
I hope anyone could help me on this.
My current configuration :
I work with wamp - apache 2.4.9 -- php 5.5.12
I work with symfony3
i add all rewrite rules in my vhost :
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.)
RewriteRule . - [e=HTTP_AUTHORIZATION:%1]
On backend, my security.yml is :
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_doc:
pattern: ^/api/doc
anonymous: true
api_login:
pattern: ^/api/login
provider: fos_userbundle
stateless: true
anonymous: true
form_login:
check_path: /api/login_check
require_previous_session: false
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
provider: fos_userbundle
lexik_jwt:
authorization_header:
enabled: true
prefix: Bearer
query_parameter:
enabled: true
name: Bearer
throw_exceptions: true
create_entry_point: true
Configuration of fosuserbundle :
fos_user:
db_driver: orm
firewall_name: main
user_class: UserBundle\Entity\User
group:
group_class: UserBundle\Entity\Group
form:
type: UserBundle\Form\Type\GroupFormType
profile:
form:
type: UserBundle\Form\Type\ProfileFormType
And side of client, i use ionic with angular js, and i try to get the token with :
var loginData = {
username: this.login.username,
password: this.login.password
};
console.debug(loginData);
$http({
url: 'http://app.local/app_dev.php/api/login_check',
method: 'POST',
data: loginData,
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
ignoreAuthModule: true
})
.success(function (data) {
console.log("Success -- login ok with ".data);
$scope.token = data;
})
.error(function (data) {
console.log("ERROR -- login fail with ", data);
$scope.token = data;
});
that's always return me 'bad credentials'
But my test with curl send me a token
curl -X POST http://app.local/api/login_check -d _username=username -d _password=pass
I have test to prefix _ my login data, change configuration
If anyone have an idea ??
EDIT :
Maybe i have found my problem.
This request curl work fine :
curl -X POST http://app.local/api/login_check -d _username=username -d _password=pass
But this one doesn't work :
curl -X POST http://app.local/app_dev.php/api/login_check -d '{"username": "user", "password": "pass"}'
I search one solution for solve it, but i guess angular http method cannot send two parameters
EDIT 2 :
This request works fine :
curl -X POST http://app.local/app_dev.php/api/login_check -d '{"_username": "user", "_password": "pass"}' -H "Content-Type: application/json"
I search a solution for copy this request with angular http object
EDIT 3 :
I make my angular request like this :
var loginData = {
_username: this.login.username,
_password: this.login.password
};
$http({
url: 'http://app.local/app_dev.php/api/login_check',
method: 'POST',
data: loginData,
headers: {'Content-Type': 'application/json'}
})
.success(function (data) {
console.log("Success -- login ok with ".data);
})
.error(function (error) {
console.log("ERROR -- login fail with ", error);
});
And my javascript console print an error :
XMLHttpRequest cannot load http://app.local/app_dev.php/api/login_check. Response for preflight has invalid HTTP status code 404
EDIT 4 :
I ddin't found the solution ...
It seems CORS request is the problem. i try do reset all headers in my mobile app, nothing has changed.
I try to install nelmioCorsBundle for authorize OPTIONS headers and control my configuration, nothing has changed
I have installed the sandbox (https://github.com/slashfan/LexikJWTAuthenticationBundleSandbox) and i have exactly the same problem.
When i try to execute curl request, i can get my token. When i use angular/ionic app, i have the error :
https://github.com/slashfan/LexikJWTAuthenticationBundleSandbox
XMLHttpRequest cannot load http://app.local/app_dev.php/api/login_check. Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8100', but only one is allowed. Origin 'http://localhost:8100' is therefore not allowed access.
If anyone have a new idea ?? i don't know what i can test now :s
PS : which version of angular is working with demo app in the LexikJWTAuthenticationBundleSandbox ?
In the first (working) request you are using _username as field with value "username" and _password as field with value "pass".
In the second you are using username (instead of _username) as field with value "user" and password (instead of _password) as field with value "pass".
Change your second request to:
curl -X POST http://app.local/app_dev.php/api/login_check -d '{"_username": "username", "_password": "pass"}'
And it should work.
EDIT
Add the option -H "Content-Type: application/json, that gives:
curl -X POST http://app.local/app_dev.php/api/login_check -H "Content-Type: application/json" -d '{"_username": "username", "_password": "pass"}'
And it should work!
EDIT
It seems to be a CORS related issue.
Try to add the following in your angular application, before doing the POST request:
app.config(function ($httpProvider) {
$httpProvider.defaults.headers.common = {};
$httpProvider.defaults.headers.post = {};
$httpProvider.defaults.headers.put = {};
$httpProvider.defaults.headers.patch = {};
});
Where app is your angular module (i.e. var app = angular.module(...)).