I need to build a kind of Java Proxy+ that handle the OAuth2 flow. The idea is to login in this "Proxy+" and do the OAuth2 flow until the Proxy+ has received the token + refresh token.
After that you login onto the Proxy+ with Username and Password or something else that give you a session. The proxy will handle generically all your web requests and add the Oauth2 token to each request. If necessary the also refresh the token.
How would you implement the Proxy Part where requests are taken and enhanced and maybe a token refresh is requested. This question is NOT about the OAuth2 flow and NOT about how to get the token in the first place.
I think i try a servlet filter that intercepts all requests and enhance the request. This way it is also generic to all urls called. Any better ideas?
Found a better way, this is the Spring way of a generic Proxy :-)
#RequestMapping("/**")
public ResponseEntity<byte[]> genericRequest(RequestEntity<?> inboundRequestEntity, HttpServletRequest request) {
URI outboundUri = UriComponentsBuilder.fromHttpUrl(this.targetBaseUrl)
.path(removeUrlPart(request))
.query(request.getQueryString())
.build(true)
.toUri();
HttpHeaders headers = filterHeaders(inboundRequestEntity.getHeaders());
BodyBuilder builder = RequestEntity
.method(requireNonNull(inboundRequestEntity.getMethod()), outboundUri)
.headers(headers);
RequestEntity<?> outboundRequestEntity = inboundRequestEntity.hasBody() ? builder.body(requireNonNull(inboundRequestEntity.getBody())) : builder.build();
try {
LOGGER.info("Will call url '{}' with method '{}'", outboundRequestEntity.getUrl(), outboundRequestEntity.getMethod());
ResponseEntity<byte[]> responseEntity = this.restTemplate.exchange(outboundRequestEntity);
return ResponseEntity.status(responseEntity.getStatusCode())
.headers(filterHeaders(responseEntity.getHeaders()))
.body(responseEntity.getBody());
} catch (
HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(filterHeaders(e.getResponseHeaders()))
.body(e.getResponseBodyAsByteArray());
}
}
private static HttpHeaders filterHeaders(HttpHeaders originalHeaders) {
HttpHeaders filteredResponseHeaders = new HttpHeaders();
filteredResponseHeaders.putAll(originalHeaders);
filteredResponseHeaders.remove(CONTENT_LENGTH);
filteredResponseHeaders.remove(DATE);
return filteredResponseHeaders;
}
Related
We have problems with obtaining ID token using auth0 SDK.
We have API Gateway based on Spring Cloud Gateway (version 3.1.4) where we try to use your auth0 platform to authenticate the users and then route the exchange to our micro services. To do it we would like to use ID Token and get email from it and pass this email to our micro services.
We log in by hitting oauth2/authorization/auth0 endpoint, we are being redirected to auth0 login page, where we provide credentials, then we get redirect back to our app.
When we configuire endpoints directly in API Gateway and mark them with #AuthenticationPrincipal OidcUser user it works and we have full user details as well as ID token.
When we proxy the exchange to different service we have Authorisation header in the request, which contains only header & signature part without payload in the ID token.
We would need the payload in ID Token in order to fetch user email for mapping the user with our internal DB in our micro services.
What do you think would be the proper workflow in this case and how can we solve this issue?
We tried to use Rules & Actions, which I paste below, but it didn’t helped us.
Our configuration looks like this:
#Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/test").authenticated()
.anyExchange().authenticated()
.and().oauth2Login()
.and().logout().logoutSuccessHandler(logoutSuccessHandler())
.and().build();
}
In the RouteLocator in GatewayConfiguration we have filter for TokenRelay.
Our Action looks like this:
exports.onExecutePostLogin = async (event, api) => {
const namespace = 'http://test.{our_local_development_route}:8888';
if (event.authorization) {
api.idToken.setCustomClaim(`${namespace}/claims/email`, event.user.email);
api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
}
};
And Rule:
function addEmailToAccessToken(user, context, callback) {
// This rule adds the authenticated user's email address to the access token.
const namespace = 'http://test.{our_local_development_route}:8888';
context.idToken[namespace + 'email'] = user.upn;
context.accessToken[namespace + 'email'] = user.email;
return callback(null, user, context);
}
I recently created a microservices architecture with Spring Cloud Gateway and Auth0. I wrote about how I created it on the Auth0 blog. That's not the interesting part. The interesting part is JHipster generates a ReactiveJwtDecoder that calls a /userinfo endpoint if some claims aren't available in the access token. This way, the access token is enriched with identity information before it's relayed to downstream microservices.
#Bean
ReactiveJwtDecoder jwtDecoder(ReactiveClientRegistrationRepository registrations) {
Mono<ClientRegistration> clientRegistration = registrations.findByRegistrationId("oidc");
return clientRegistration
.map(oidc ->
createJwtDecoder(
oidc.getProviderDetails().getIssuerUri(),
oidc.getProviderDetails().getJwkSetUri(),
oidc.getProviderDetails().getUserInfoEndpoint().getUri()
)
)
.block();
}
private ReactiveJwtDecoder createJwtDecoder(String issuerUri, String jwkSetUri, String userInfoUri) {
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return new ReactiveJwtDecoder() {
#Override
public Mono<Jwt> decode(String token) throws JwtException {
return jwtDecoder.decode(token).flatMap(jwt -> enrich(token, jwt));
}
private Mono<Jwt> enrich(String token, Jwt jwt) {
// Only look up user information if identity claims are missing
if (jwt.hasClaim("given_name") && jwt.hasClaim("family_name")) {
return Mono.just(jwt);
}
// Retrieve user info from OAuth provider if not already loaded
return users.get(
jwt.getSubject(),
s -> {
WebClient webClient = WebClient.create();
return webClient
.get()
.uri(userInfoUri)
.headers(headers -> headers.setBearerAuth(token))
.retrieve()
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
.map(userInfo ->
Jwt
.withTokenValue(jwt.getTokenValue())
.subject(jwt.getSubject())
.audience(jwt.getAudience())
.headers(headers -> headers.putAll(jwt.getHeaders()))
.claims(claims -> {
String username = userInfo.get("preferred_username").toString();
// special handling for Auth0
if (userInfo.get("sub").toString().contains("|") && username.contains("#")) {
userInfo.put("email", username);
}
// Allow full name in a name claim - happens with Auth0
if (userInfo.get("name") != null) {
String[] name = userInfo.get("name").toString().split("\\s+");
if (name.length > 0) {
userInfo.put("given_name", name[0]);
userInfo.put("family_name", String.join(" ", Arrays.copyOfRange(name, 1, name.length)));
}
}
claims.putAll(userInfo);
})
.claims(claims -> claims.putAll(jwt.getClaims()))
.build()
);
}
);
}
};
}
Please let me know if this is not possible...but in an effort to refactor my personal API I decided to start calling the Twitch endpoints through my API so data can be combined. To do this I direct the user to the auth page and get a bearer token back. I then pass that token to my API in the header. For some reason I get a 401 if I try to use that token at all from my API. I have no idea why as I can't view a reason in the response. The token works from postman.
Here is an example of a request I make in my API:
public async Task<bool> ValidateToken()
{
var response = await client.GetAsync("https://id.twitch.tv/oauth2/validate");
return response.StatusCode == HttpStatusCode.OK;
}
The HttpClient is created as follows before the validation method is called:
public TwitchService(IHeaderDictionary headers)
{
StringValues token;
StringValues clientId;
var hasToken = headers.TryGetValue("Authorization", out token);
var hasClientId = headers.TryGetValue("Client-id", out clientId);
client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
if (hasToken)
{
var authToken = token.ToString().Replace("Bearer", "");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
}
if (hasClientId)
{
client.DefaultRequestHeaders.Add("Client-ID", clientId.ToString());
}
}
It turns out that the auth header is removed by the HttpClient and this is by design. The following link gives a good explanation about it: Authorization header is lost on redirect
I am implementing a client with Spring Security OAuth2 to use resources through an API, I have to customize the request like this:
POST https://example.com/v2/oauth2/token HTTP / 1.1
Authorization: Basic xxxXXXXxxXXXXXXxXXxxXX
Content-Type: application / x-www-form-urlencoded
Accept: application / json; charset = UTF-8
grant_type = authorization_code & code = 12345678901234567890
Where: grant_type = authorization_code & code = 12345678901234567890
it is contained in the body as raw data.
How can I put the grant_type and code parameters on the body?
At the moment my code is formalized in this way:
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails ();
resource.setAccessTokenUri ( "https://example.com/v2/oauth2/token");
resource.setClientId ( "xxxxx");
resource.setClientSecret ( "xxxxx");
resource.setGrantType ( "authorization_code");
resource.setUseCurrentUri (false);
AccessTokenRequest atr = new DefaultAccessTokenRequest ();
atr.setPreservedState (new Object ());
atr.setAuthorizationCode(authCode);
AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider ();
try {
OAuth2AccessToken accessToken = provider.obtainAccessToken (resource, atr);
}
catch (Exception e) {
System.out.println ("DEBUG" + e);
}
Maybe you can use a filter to intercept the request to the token endpoint before the authorization server completes the process of authorization.
This is my implementation of OAuth2 using a json as a body instead of URL-encoded format.
https://github.com/aldwindelgado/spring-boot-oauth2-server/blob/master/src/main/java/com/github/oauth2/server/JsonToUrlEncodedAuthenticationFilter.java
Add atr.setAuthorizationCode("12345678901234567890"); to set the auth code to the Access Token Request.
Trying to retrieve the OAuth2.0 AccessToken using DotNetOpenAuth library, coded the same as below -
https://github.com/DotNetOpenAuth/DotNetOpenAuth/wiki/Security-scenarios
private static IAuthorizationState GetAccessTokenFromOwnAuthSvr()
{
var server = new AuthorizationServerDescription();
server.TokenEndpoint = new Uri("https://localhost/STS/OAuth/Token");
server.ProtocolVersion = DotNetOpenAuth.OAuth2.ProtocolVersion.V20;
var client = new UserAgentClient(server, clientIdentifier: "RP");
client.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter("data!");
var token = client.ExchangeUserCredentialForToken(
"Max Muster", "test123", new[] { "http://localhost/demo"});
return token;
}
This is not working as AuthZ Server returns error, complaining about the missing redirect_uri.
Going through few of the links here, saw that an AuthorizationState has the redirect_uri option, but could not figure out how to pass the AuthorizationState object in the ExchangeUserCredentialForToken request.
Is there a way to send the redirect_uri parameter or the AuthorizationState object as part of the ExchangeUserCredentialForToken request ?
Thanks in advance
--Abraham V K
Am currently developing an Authorization server using Owin, Oauth, Claims.
Below is my Oauth Configuration and i have 2 questions
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(1000),
Provider = new AuthorizationServerProvider()
//RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
If the token is expired and user accessing using the expired token user is getting 401(unAuthorized).Checking using Fiddler.
How can i send a customized message to an user stating your token as expired. Which function or module i need to override.
and my another quesiton is What is the use of the below line ?
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
Do i really need this to implement because when i checked it still works without the above line. Any security violation ?
You can't directly customize the behavior for expired tokens but you can do that with a custom middleware.
First override the AuthenticationTokenProvider so that you can intercept the authentication ticket before it is discarded as expired.
public class CustomAuthenticationTokenProvider : AuthenticationTokenProvider
{
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
if (context.Ticket != null &&
context.Ticket.Properties.ExpiresUtc.HasValue &&
context.Ticket.Properties.ExpiresUtc.Value.LocalDateTime < DateTime.Now)
{
//store the expiration in the owin context so that we can read it later a middleware
context.OwinContext.Set("custom.ExpriredToken", true);
}
}
}
and configure it in the Startup along with a small custom middleware
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AccessTokenProvider = new CustomAuthenticationTokenProvider()
});
//after the request has been authenticated or not
//check for our custom env setting and act accordingly
app.Use(new Func<AppFunc, AppFunc>(next => (env) =>
{
var ctx = new OwinContext(env);
if (ctx.Get<bool>("custom.ExpriredToken"))
{
//do wathever you want with the response
ctx.Response.StatusCode = 401;
ctx.Response.ReasonPhrase = "Token exprired";
//terminate the request with this middleware
return Task.FromResult(0);
}
else
{
//proceed with the rest of the middleware pipeline
return next(env);
}
}));
If you have noticed I've placed the custom middleware after the call to UseOAuthBearerAuthentication and this is important and stems from the answer to your second question.
The OAuthBearerAuthenticationMidlleware is responsible for the authentication but not for the authorization. So it just reads the token and fills in the information so that it can be accessed with IAuthenticationManager later in the pipeline.
So yes, with or without it all your request will come out as 401(unauthorized), even those with valid tokens.