How to get JWT token for current Liferay session - jwt

I have configured Liferay v7.3.4 CE to authenticate with AWS Cognito using OpenID Connect Provider, and that all works fine.
I would now like to invoke REST APIs in AWS, from within Liferay, using the JWT token obtained from Cognito during the sign-in process.
It would seem this JWT token should be available within Liferay, correct? If so, a source code example demonstrating how to access this would be very much appreciated.
This token would then be added to the Authorization header of API calls to an instance of the AWS API Gateway secured by the same Cognito instance from which the user has just signed in. But first things first... how would someone programmatically access the JWT token for the current Liferay session?
Hope this makes sense.

I've got this working.
First, I am using Maven (not gradle) to build Liferay projects. To this end, I've added the following to my portlet's pom.xml file:
<dependency>
<groupId>com.liferay</groupId>
<artifactId>com.liferay.portal.security.sso.openid.connect.api</artifactId>
<scope>provided</scope>
</dependency>
Next, in my portlet's render method, I've added the following code:
public void render(RenderRequest renderRequest, RenderResponse renderResponse) throws PortletException, IOException
{
try {
// get the jwtToken from the renderRequest parameter
String jwtToken = null;
HttpSession session = PortalUtil.getOriginalServletRequest(PortalUtil.getHttpServletRequest(renderRequest)).getSession();
if (session.getAttribute(OpenIdConnectWebKeys.OPEN_ID_CONNECT_SESSION) instanceof OpenIdConnectSession) {
OpenIdConnectSession openIdConnectSession = (OpenIdConnectSession) session.getAttribute(OpenIdConnectWebKeys.OPEN_ID_CONNECT_SESSION);
jwtToken = openIdConnectSession.getAccessTokenValue();
}
// call a REST API with the jwt token
List<Organization> organizations = masterDataClient.fetchOrganizations(jwtToken);
// do other stuff
super.render(renderRequest, renderResponse);
} catch (Exception e) {
throw new PortletException(e);
}
}

Related

Keycloak.js can not get access token when login with Google and Facebook

I'm build a system for our company using Keycloak. I use keycloak.js for by-pass default login page of Keycloak.
function myFunction() {
let kcLogin = keycloak.login;
keycloak.login = (options) => {
options.idpHint = 'facebook';
kcLogin(options).then(auth => {
alert("keycloak Login");
if(auth) {
alert("token" + kc.token);
} else {
alert("auth is null");
}
});
};
keycloak.init({ onLoad: 'login-required' }).then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
}
But I can NOT get access token / refresh token after login.
I check: keycloak.token = undefined.
Please help me!
PS: I always get exception of keycloak.init then redirect to facebook login ( or google login )
alert('failed to initialize');
Thank you so much.
Code here: https://github.com/loizenai/SpringBoot-Keycloak-Social-Authentication-Py-Pass-Default-Login/tree/main/SpringBootKeyCloakSocialSignIn
You are trying to configure your backend and frontend with Keycloak.
Either you just have to configure your backend to integrate with your keycloak or Integration your frontend application and your backend will only verify the token.
The current application architecture you are following is an MVC pattern.
Where your spring boot(backend) application controls the integration with Keycloak.
Please refer to this article: Secure spring boot 2 using Keycloak
What you are trying to target follows this kind of architecture pattern:
Secure Vue.js apps with Keycloak | DevNation Tech Talk
In the above reference, I have used the Vue application but you can use your vanilla html/js application as well to integrate with keycloak.
First, try keycloak login flow in your application and then you can enable social login.

.Net Core Web API Bearer The issuer is invalid

I have written a Blazor WASM app based on the latest Microsoft template. In development mode it all works great but after publishing it to Azure App Service I randomly get a 401 unauthorised when calling the API, looking at the returned headers I get
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://*domain*.azurewebsites.net' is invalid"
This is when the client is using the https://domain.azurewebsites.net client. So it matches the web API.
I also have a custom domain attached to the app service, this means there is also https://www.domain.co.uk and https://domain.co.uk both are SSL'd.
I have checked the JWT token and it contains the correct URL for the version of the website I am calling.
Sometimes everything works but 60% of the time it allows the user to login and then fails on the API calls. I can't seem to track it to 1 domain name or pattern like expired logins. If you log out and then log back in, it doesn't clear the issue.
The configure looks like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
Any help or hints in the right direction is appreciated
Cheers
Dave
In my case it was caused by Linux environment of App Service. Now documentation has a clear note on that:
For Azure App Service deployments on Linux, specify the issuer explicitly in Startup.ConfigureServices.
This is how I set it:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "https://my-site.azurewebsites.net";
#if DEBUG
options.Authority = "https://localhost:5001";
#endif
});

How to add policy to Keycloak - UI crashes

I'm trying to enable flow when some admin user by some admin client is able to create users and obtain their access tokens to be used for another clients.
I have KeyCloak setup with token exchange and fine grained authz enabled and configured clients. I'm able to login my admin user by REST api, then exchange token. But when I specify audience I got error.
This one returns token but I need token for another client/audience.
http -f POST https://my-keycloak-server.com/auth/admin/realms/my-realm/protocol/openid-connect/token grant_type=urn:ietf:params:oauth:grant-type:token-exchange requested_subject=1a147915-53fe-454d-906a-186fecfa6974 client_id=api-admin client_secret=23a4ecbe-a9e8-448c-b36a-a45fa1082e6e subject_token=eyJhbGeiOiJSUzI1NiIs......
This one is failing with error.
http -f POST https://my-keycloak-server.com/auth/admin/realms/my-realm/protocol/openid-connect/token grant_type=urn:ietf:params:oauth:grant-type:token-exchange requested_subject=1a147915-53fe-454d-906a-186fecfa6974 client_id=api-admin client_secret=23a4ecbe-a9e8-448c-b36a-a45fa1082e6e subject_token=eyJhbGeiOiJSUzI1NiIs...... audience=my-another-client
{
"error": "access_denied",
"error_description": "Client not allowed to exchange"
}
So I tried to setup fine grained auth for target audience client (enabled it in tab, then tried to add policy for my admin user to be able to exchange token) but when I want to add policy that will allow my admin user to perform token exchange I'm stuck on UI error.
When typing policy name I got 404 when Keycloak is looking for name colisions. Afaik 404 in this case shouldn't block form from posting because it is no name collision. Instead I got instantly redirected with error.
https://my-keycloak-server.com/auth/admin/realms/my-realm/clients/1bafa9a4-f7e2-422c-9188-58ea95db32ef/authz/resource-server/policy/search?name=some-name
In the end of the day I can't add any policy in Keycloak. All the time form validation is ending up with crash caused by 404 policy name not found.
I'm using dockerized keycloak 10.0.0
Any ideas?
I hacked it by live editing Angular JS UI script function that performs verification in line 2403.
this.checkNameAvailability = function (onSuccess) {
if (!$scope.policy.name || $scope.policy.name.trim().length == 0) {
return;
}
ResourceServerPolicy.search({
realm: $route.current.params.realm,
client: client.id,
name: $scope.policy.name
}, function(data) {
if (data && data.id && data.id != $scope.policy.id) {
Notifications.error("Name already in use by another policy or permission, please choose another one.");
} else {
onSuccess();
}
});
}
to
this.checkNameAvailability = function (onSuccess) {
onSuccess();
}
And that end up with successfuly added policy. Still looks like it's UI bug.

Spring Boot OAuth 2 SSO how to extract token to pass back to thick client application

I have a thick client application (C# but that should not matter).
All the users already exist in an authentication/authorization (3rd party) system that provides OAuth 2 API (authorize/access_token plus a user_info service).
I have a Spring Boot web service tier that will have RESTful web services that will be called by the thick client application that must only be called by authenticated users for protected web services.
To authenticate the thick client will launch a Web Browser (OS installed default) and will open https to restful.web.server:8443 /login of the Spring Boot web service tier. This will do the OAuth 2 (authorization_code) interaction. Once redirected back with a valid token I want to redirect to a custom URI passing the token and for the browser to close (if possible) so an OS registered application can extract the token and pass it via an IPC mechanism to the thick client application.
The thick client application can then pass the token to the Web Services in the header (Authorize: TOKEN_TYPE TOKEN_VALUE).
The Web Services must then validate the authenticity of the token.
The Web Services if called with an invalid token must just return an HTTP error and JSON error content (e.g. code+message) and not try and redirect to the login screen. This will be orchestrated by the thick client application.
I have no concern with any of the custom URI handling, IPC development, or thick client web service calls. It is all the Spring/SSO magic in getting the token to be sent to my thick client and returning the relevant error from protected web services without returning a redirect to the SSO login.
I appear to be authenticating and being sent a token but then I get an exception.
I have made some progress and it appears that by manually launching a browser and hitting my web service tier https to restful.web.server:8443 /login it redirects to the SSO site https to 3rdparty.sso.server /oauth/authorization (passing in client_id, redirect_uri, response_type=code, state). I can log in, and Spring is calling the https to 3rdparty.sso.server /oauth/access_token endpoint (I had to create a custom RequestEnhancer to add in Authorization: Basic ENCODED_CLIENT_ID_AND_CLIENT_SECRET to satisfy the access_token SSO API requirement).
This returns 200 OK but then I get exceptions and do not know how to extract the token. The access_token returned may not be using the standard property names but unsure when to go and check if this is the case. I done the authentication this way to keep the client id and client secret out of the thick client application and my web services must do the authorisation anyway. If there is a better way or pointers to someone else doing this already it would be greatly appreciated. I find so many examples that are either not quite relevant or more towards web applications.
server:
port: 8443
ssl:
key-store: classpath:keystore.p12
key-store-password: **********
keyStoreType: PKCS12
keyAlias: tomcat
servlet:
context-path: /
session:
cookie:
name: UISESSION
security:
basic:
enabled: false
oauth2:
client:
clientId: *******
clientSecret: *****************
accessTokenUri: https://3rdparty.sso.server/oauth2/access_token
userAuthorizationUri: https://3rdparty.sso.server/oauth2/authorize
authorizedGrantTypes: authorization_code,refresh_token
scope:
tokenName: accessToken
redirectUri: https://restful.web.server:8443/login
authenticationScheme: query
clientAuthenticationScheme: header
resource:
userInfoUri: https://3rdparty.sso.server/oauth2/userinfo
logging:
level:
org:
springframework: DEBUG
spring:
http:
logRequestDetails: true
logResponseDetails: true
#Configuration
#EnableOAuth2Sso
#Order(value=0)
public class ServiceConectWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// From the root '/' down...
.antMatcher("/**")
// requests are authorised...
.authorizeRequests()
// ...to these url's...
.antMatchers("/", "/login**", "/debug/**", "/webjars/**", "/error**")
// ...without security being applied...
.permitAll()
// ...any other requests...
.anyRequest()
// ...the user must be authenticated.
.authenticated()
.and()
.formLogin().disable()
.logout()
.logoutSuccessUrl("/login")
.permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
;
}
I expect that the secured web services would be accessible once authenticated via the browser whilst testing without the client and would not expect exceptions to be thrown. I need to be able to extract the returned token and pass it back to my thick client.
Redirects to 'https://3rdparty.sso.server/oauth2/authorize?client_id=***HIDDEN_CLIENT_ID***&redirect_uri=https://localhost:8443/login&response_type=code&state=***HIDDEN_STATE_1***'
Then FilterChainProxy : /login?code=***HIDDEN_CODE_1***&state=***HIDDEN_STATE_1*** at position 6 of 12 in additional filter chain;
Request is to process authentication
RestTemplate : HTTP POST https://3rdparty.sso.server/oauth2/access_token
RestTemplate : Response 200 OK
IllegalStateException: Access token provider returned a null access token, which is illegal according to the contract.
at OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:223) ```
Then end up at an error page
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Internal Server Error, status=500).
Access token provider returned a null access token, which is illegal according to the contract.
The access_token service was returning non-standard JSON names.
I created a MyOwnOAuth2AccessToken with the relevant non-standard JSON names the necessary de/serialisation classes.
I created a MyOauth2AccesTokenHttpMessageConverter class for returning my OAuth2AccessToken.
The MyOauth2AccesTokenHttpMessageConverter was plumbed in from an
#Configuration
public class ServiceConnectUserInfoRestTemplateFactory implements UserInfoRestTemplateFactory
within the
#Bean
#Override
public OAuth2RestTemplate getUserInfoRestTemplate()
method with the following code:
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new ItisOAuth2AccessTokenHttpMessageConverter());
messageConverters.addAll((new RestTemplate()).getMessageConverters());
accessTokenProvider.setMessageConverters(messageConverters);
There is probably a better way to do this but this worked for me.

Custom Authentication - Spring boot 403 forbidden error

I was trying it implement custom authentication, Authentication works fine, but have problems with Authorization. I am using JWT tokens, Any API I try to access it throwing me a 403 forbidden error. I am not sure what is wrong. I have the full source code in github. https://github.com/vivdso/SpringAuthentication, Spring boot magic is not working on this. Any pointers are apperciated.
Using MongoDb as my repository to store user accounts and roles.
InMemory Authentication is working fine, but Custom Authentication always returs 403, Below is my I extended WebSecurityConfigurerAdapter
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// authenticationManagerBuilder.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
// authenticationManagerBuilder.inMemoryAuthentication().withUser("user").password("user").roles("USER");
authenticationManagerBuilder.authenticationProvider(getCustomAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/customer").hasAuthority("ADMIN")
.antMatchers(HttpMethod.GET, "/order").hasAuthority("USER").and()
.csrf().disable();
}
#Bean
protected CustomAuthenticationProvider getCustomAuthenticationProvider(){
return new CustomAuthenticationProvider();
}
I don't have any custom implementation for authorization.
The issue is resolved, I have updated Github repository. The spring boot security was working fine, the issue was the roles assigned to the user collection was a Json string object (e.g. {"role":"ROLE_ADMIN"}) instead of sting object "ROLE_ADMIN".
Thanks