org.keycloak.common.VerificationException: Invalid token issuer - keycloak

I'm developing an Android app, which uses my REST backend. The backend is running on an JBoss instance, which is secured through Keycloak. Since I've updated my Keycloak from 1.0.7 to 2.1.5 I'm experiencing the following problem.
If I try to call a REST API of my backend, JBoss writes the folowing log:
[org.keycloak.adapters.BearerTokenRequestAuthenticator] (default task-39)
Failed to verify token: org.keycloak.common.VerificationException: Invalid token issuer.
Expected 'http://localhost:8180/auth/realms/myrealm', but was 'http://192.168.178.24:8180/auth/realms/myrealm'
at org.keycloak.TokenVerifier.verify(TokenVerifier.java:156)
at org.keycloak.RSATokenVerifier.verify(RSATokenVerifier.java:89)
192.168.178.24 is the right IP address. It seems to be a configuration issue, but where can I config this address?
Has anybody an idea how to fix this problem?

Very simple solution: Make sure when any of your component contact with Keycloak server, they use the same url.
Detailed explanations:
For your case (same as mine), it seems that your Android app is making http request to http://192.168.178.24:8180/... while your server is requesting (or at least configured to) http://192.168.178.24:8180/.... So change your server such that it will request http://192.168.178.24:8180/....
P.S. The exception seems to be the expected behavior to avoid some attacks.

If you take a look into the implementation, here it throws your Exception.
public static class RealmUrlCheck implements Predicate<JsonWebToken> {
private static final RealmUrlCheck NULL_INSTANCE = new RealmUrlCheck(null);
private final String realmUrl;
public RealmUrlCheck(String realmUrl) {
this.realmUrl = realmUrl;
}
#Override
public boolean test(JsonWebToken t) throws VerificationException {
if (this.realmUrl == null) {
throw new VerificationException("Realm URL not set");
}
if (! this.realmUrl.equals(t.getIssuer())) {
throw new VerificationException("Invalid token issuer. Expected '" + this.realmUrl + "', but was '" + t.getIssuer() + "'");
}
return true;
}
};
I think your Client configuration is not correct. Do you have the same clients as in your Keycloak?

Related

Does spring-boot-admin support sso?

I'm trying to integrate spring-boot-admin with my corporate SSO, does spring-boot-admin support sso login? I cannot find documentation about it.
I got it to work. Steps to implement:
Create controller with an endpoint that the sso provider will call.
In the endpoint, put the logic for sso integration,
On success, redirect to /applications
On failure, throw an exception
#Controller
public class SsoIntegration {
// this does addition authentication stuff, like sets up the
// right authorities...etc
#Autowired
private AuthenticationProvider authenticationProvider;
// my sso provider creates a vanity url that redirects to
// this endpoint and passes 2 request params using POST
#RequestMapping(value={"/sso"}, method = {RequestMethod.POST})
public String ssologin (
#RequestParam(name="param1") String param1,
#RequestParam(name="param2") String param2 )
{
// do your sso integration logic here
// eg...
SsoUtil util = new SsoUtil();
String userInfo = util.decrypt(param1, param2, ...);
...
if (authenticationProvider.authenticate( userInfo )) {
Authentication postAuthentication = ...// populate your successfully authenticated user
// these next lines are the important stuff
// 1. set the postAuthentication into the security context
SecurityContextHolder.getContext().setAuthentication(postAuthentication);
// 2. redirect to the applications page
return "redirect:/applications";
}
// authentication failed, throw an exception...
throw new RuntimeException ("Sso Authentication failed");
}
}

asp.net core redirect to https

We have a multi-tenant asp.net core 2 application.
We need to redirect to their https the requests to non-secure url. We have to do this by code instead using the web.config or IIS, since some domains have https and some others not. We check which domain needs this redirection by checking in a database based on the host making the request.
Is it possible to do this in a middleware?
Thanks in advance.
I found a way to do what we need and I'd like to share in case someone else has the same problem.
We have solved it with URL Rewrite rules using the Microsoft.AspNetCore.Rewrite library.
In the startup.cs we add the following code
app.UseRewriter(new RewriteOptions().Add(new RedirectToSecureRule()));
Then we add the class RedirectToSecureRule
public class RedirectToSecureRule : IRule
{
public virtual void ApplyRule(RewriteContext context)
{
var currentRequest = context.HttpContext.Request;
if (currentRequest.IsHttps)
{
context.Result = RuleResult.ContinueRules;
return;
}
if (currentRequest.Host.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
context.Result = RuleResult.ContinueRules;
return;
}
if (context.HttpContext.Items["TenantSsl"] == null || !Convert.ToBoolean(context.HttpContext.Items["TenantSsl"]))
{
context.Result = RuleResult.ContinueRules;
return;
}
var httpsUrl = UriHelper.BuildAbsolute("https", new HostString(currentRequest.Host.Value), currentRequest.PathBase, currentRequest.Path, currentRequest.QueryString);
var response = context.HttpContext.Response;
response.StatusCode = 301;
response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Location] = httpsUrl;
context.Result = RuleResult.EndResponse;
}
}
As you see if the request is already https we don't do anything. The same for localhost, although this condition is not necessary since we can also work with https in localhost.
Then we check if HttpContext.Items["TenantSsl"] is true. This is because our app is a multi-tenant app where we share the codes and the IIS pool but each tenant has its own database.
It could happen that one tenant has a SSL certificate and others not, that's the reason we have to add a field in our tenant's database to check that.
Before this Rule is executed, we execute the tenant selector, where we add to the HttpContext.Items a bool variable indicating is the current tenant has a SSL certificate-
Finally, if the tenant has a SSL certificate and the request is not https, we redirect to the https page.
May thanks to Ray Huang, we based this solution on one of his posts:
https://blog.discountasp.net/3-ways-to-redirect-http-to-https-and-non-www-to-www-in-asp-net-core/

How to prevent browser from sending NTLM credentials?

I’m working on a site where we want to use Kerberos authentication using Spring Security Kerberos. So, we don’t support NTLM. When the user makes an unauthenticated request, the server will reply with an HTTP 401 with header WWW-Authenticate: Negotiate.
The problem:
For some users/configurations, the browser will send NTLM credentials. The server is not necessarily running on Windows so it can’t handle the NTLM credentials.
As I understand, “Negotiate” means “please send me Kerberos if possible, or else send NTLM”. Is there a different setting that says “only send me Kerberos”? Or is there some way to tell the browsers the site only supports Kerberos?
As a follow-up, why would the browser not have Kerberos available? In this case they are logged in to the same domain. Maybe their credentials have expired?
Kerberos and Spnego should not be confused. Though Spnego is often used for Kerberos authentication, Spnego does not always mean Kerberos, or even a preference for Kerberos.
Spnego is a protocol that allows client and server to negotiate a mutually acceptable mech type (if available).
That may or may not be Kerberos depending on the sub-mechanisms requested by the client and server during the negotiation process.
The Negotiation process may take several handshake attempts.
Using human languages as an example. If I speak English, Latin and Zulu, in that order of preference, and you speak Eskimau and Zulu, then we will end up speaking Zulu.
In the setup that I am currently testing, with Internet Explorer as a client, and a custom Java Application Server using JAAS + GSS as the Server I observe similar behavour to that in your comment:
Browser sends an unauthenticated request
Server replies with HTTP 401 Unauthorized, WWW-Authenticate: Negotiate header.
Browser either responds with Negotiate + NTLM token (bad!).
In my case the game does not end there, it continues as follows:
Server replies with HTTP 401 Unauthorized, WWW-Authenticate: Negotiate + GSS response token
Browser responds with Negotiate + Spnego NegoTokenTarg wrapping a Kerberos Token.
Server unwraps the Kerberos Token; decodes, and authenticates the client; responds with HTTP 200, WWW-Authenticate: Negotiate + GSS response token
i.e. I don't prevent the browser sending an NTLM token, my Server just continues negotiation for another round until it gets a Kerberos Token.
As a side issue: the token provided by Internet Explorer 11 at step 3. above is not properly Spnego compliant, it is neither a NegTokenInit, nor a NetTokenTarg, and at 127 bytes long is clearly much too short to be or wrap a Kerberos token.
You are using Spring Security Kerberos, but in a comment you indicate an interest in other libraries, so below is my JGSS based Spnego authentication code.
For brevity I leave out the JAAS setup, but all this takes place in a JAAS Subject.doAs() privileged context.
public static final String NEGOTIATE = "Negotiate ";
public static final String AUTHORIZATION = "Authorization";
public static final String WWWAUTHENTICATE = "WWW-Authenticate";
public static final int HTTP_OK = 200;
public static final int HTTP_GOAWAY = 401; //Unauthorized
public static final String SPNEGOOID = "1.3.6.1.5.5.2";
public static final String KRB5OID = "1.2.840.113554.1.2.2";
public void spnegoAuthenticate(Request req, Response resp, Service http) {
GSSContext gssContext = null;
String kerberosUser = null;
String auth =req.headers("Authorization");
if ( auth != null && auth.startsWith(NEGOTIATE )) {
//smells like an SPNEGO request, so get the token from the http headers
String authBody = auth.substring(NEGOTIATE.length());
int offset =0;
// As GSS cannot directly process Spnego NegTokenInit and NegTokenTarg, preprocess and extract native Kerberos token.
authBody = preProcessToken(authBody);
try {
byte gssapiData[] = Base64.getDecoder().decode(authBody);
gssContext = initGSSContext(SPNEGOOID, KRB5OID);
byte token[] = gssContext.acceptSecContext(gssapiData, offset, gssapiData.length);
if (gssapiData.length > 128) {
//extract the Kerberos User. The Execute/Login service will compare this with the user in the message body.
kerberosUser = gssContext.getSrcName().toString();
resp.status(HTTP_OK);
} else {
//Is too short to be a kerberos token (or to wrap one), so don't try and extract the user.
//This could be a first pass from an SPNEGO enabled Web-browser. Maybe NTLM?
resp.status(HTTP_GOAWAY);
}
String responseToken = Base64.getEncoder().encodeToString(token);
if (responseToken != null && responseToken.length() > 0) {
resp.header(WWWAUTHENTICATE, NEGOTIATE + responseToken);
}
} catch (GSSException e) {
// Something went wrong fishing the token from the http headers
http.halt(401, "Go Away! This is a privileged route, and you ain't privileged!"+"\r\n");
} finally {
try {
gssContext.dispose();
} catch (GSSException e) {
//error handling here
}
}
} else {
//This is either not a SPNEGO request, or is the first pass without token
resp.header(WWWAUTHENTICATE, NEGOTIATE.trim()); //set header to suggest negotiation
http.halt(HTTP_GOAWAY, "Go Away! This is a privileged route, and you ain't privileged! Only come back when you are."+"\r\n");
}
}
private String preProcessToken(String authBody) {
String tag = getTokenType(authBody);
if (tag.equals("60")) {
// is a standard "application constructed" token. Kerberos tokens seem to start with "YI.."
} else if (tag.equals("A0")) {
// is a Spnego NegTokenInit, starting with "oA.." to "oP.."
authBody=extractKerberosToken(authBody);
} else if (tag.equals("A1")) {
// is a Spnego NegTokenTarg, starting with "oQ.." to "oZ.."
authBody=extractKerberosToken(authBody);
} else {
// some other unexpected token.
// TODO: generate error
}
return authBody;
}
private String extractKerberosToken(String authBody) {
return authBody.substring(authBody.indexOf("YI", 2));
}
private String getTokenType(String authBody) {
return String.format("%02X", Base64.getDecoder().decode(authBody.substring(0,2))[0]);
}
Note this code is presented "as-is", as an example. It is work-in-progress and has a number of flaws:
1) getTokenType() uses the decoded token, but extractKerberosToken works on the encoded token, both should use byte operations on the decoded token.
2) Token rejection based on length is a little too simple. I plan to add better NTLM token identification....
3) I don't have a true GSS context loop. If I don't like what the client presents, I reject and close the context.
For any following handshake attempts from the client I open a new GSS context.

Angular 2 api call responds with 404 even though service works when tested

I am trying to call a web service from my Angular 2 app.
private baseUrl: string = 'http://localhost:3000/api';
getPatterns(): Observable<Pattern[]> {
const patterns$ = this.http
.get(`${this.baseUrl}/pattern`, {headers: this.getHeaders()})
.map(this.mapPatterns)
.catch(this.handleError);
return patterns$;
}
private getHeaders() {
const headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
This gives me a 404 error for URL: http://localhost:3000/api/pattern even though I get a valid response when I open the URL in browser or try to call it from POSTMAN.
Any help pointing out why this doesn't work would be appreciated.
Thanks!
Vetemi's answer solved the first step of this issue for me. I had built my Angular App following along with the Angular Tour Of Heroes tutorial and removing the dependencies on the In Memory web api service resolved the 404 error. After that I was getting a CORS error, specifically the error read:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
My API is a .Net Core 2.0 API, so I needed to enable CORS which I did following the steps at this link
https://learn.microsoft.com/en-us/aspnet/core/security/cors
This is a trimmed down version of my Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(builder => builder.AllowAnyOrigin());
app.UseMvc();
}
In case, you haven't found the problem and for those who have the same problem.
Have you used the in memory database from the tutorial Angular Tour of Heroes? If yes, then this may be the problem. The dependency
"angular-in-memory-web-api": "~0.2.4",
intercepts your http requests. Removing this dependency might help. Solution was found here.
Your header contains nothing, so the response is 404. See the below change
private getHeaders() : Headers {
const headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
Reason: Default return type of the methods are void, so when you are returning you need to explicitly have the return type

504 error accessing Kinvey handshake(Rest api)

I have been trying to get the Kinvey handshake for the REST api to work for a while now but have not had any luck. I am using libgdx's net class to send the http request. Wverytime I send the request I get a 504(Gateway Timeout) error. I am following the instructions on the website so I am not sure why I would get that error.
Here is my attempt:
HttpRequest request = new HttpRequest(HttpMethods.GET);
request.setHeader("GET", "/appdata/:App_key");
request.setHeader("Host:", "baas.kinvey.com");
String authHeader = "Basic " + Base64Coder.encodeString("App_key:App_secret");
request.setHeader("Authorization:", authHeader);
request.setUrl("https://baas.kinvey.com/appdata/App_key");
System.out.println("HTTP REQUEST: " + request.getHeaders());
responseListener listener = new responseListener() {
public void handleHttpResponse (HttpResponse httpResponse) {
HttpStatus status = httpResponse.getStatus();
if (status.getStatusCode() >= 200 && status.getStatusCode() < 300) {
System.out.println("HTTP SUCCESS!");
} else {
System.out.println("HTTP ERROR: " + status.getStatusCode());
}
System.out.println("HTTP :" + httpResponse.getResultAsString());
}
#Override
public void failed(Throwable t) {
t.printStackTrace();
System.out.println("REQUEST FAILED!" +t.getMessage());
super.failed(t);
}
};
Gdx.net.sendHttpRequest(request, listener);
As far as I can tell, there is something wrong with the header. I have tested the Url which takes me to a login screen. The login works after I put in the App key as the user name and the Master secret as the password. Is there something obviously wrong? Is there a way I can debug this further?
I'm an engineer at Kinvey and can help you out with this.
A couple things:
first, there are some extra headers there that you don't need. While they might not be the cause of the issue, it is still safe to remove:
request.setHeader("GET", "/appdata/:App_key");
request.setHeader("Host:", "baas.kinvey.com");
Note that GET is set when you create the HttpRequest, and Host is set when you define the URL.
Second, get rid of the colon after "authorization" when setting your header, make it look like this:
request.setHeader("Authorization", authHeader);
Also, you mention that it works with your master secret but not with your app secret? Can you ensure that you are base64 encoding both?
One last thing-- ensure that you replace App_Key with your actual app key, in the URL as well as in the headers.