Spring Security OAuth2 Credentiais through header - spring-security-oauth2

Well, i'm creating a Authorization Server using Spring Security OAuth Project, this is my configurer class:
#Configuration
public class AuthConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
public AuthConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientid")
.secret(passwordEncoder().encode("secret"))
.authorizedGrantTypes("authorization_code", "client_credentials", "password")
.scopes("myscope")
.redirectUris("http://localhost:8080/oauth/login/client-app");
}
/**
* Precisamos para uso do Password Flow
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).
tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter());
}
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("ABC");
return jwtAccessTokenConverter;
}
}
Well, when I try to retrieve the Access Token i need pass "clientid" and "secret" through headers, like this (works very well returning the JWT Token):
curl clientid:secret#localhost:8080/oauth/token -dgrant_type=client_credentials -dscope=myscope
But if I try this:
curl localhost:8080/oauth/token -dgrant_type=client_credentials -dscope=transferir-valores -dclient_id=clientid -dclient_secret=secret
I got Unauthorized message.

The second request is incorrect and that is why you're getting an unauthorized message.
Check the following excerpt from the specification:
Clients in possession of a client password MAY use the HTTP Basic
authentication scheme as defined in [RFC2617] to authenticate with the
authorization server. The client identifier is encoded using the
"application/x-www-form-urlencoded"
Alternatively, the authorization server MAY support including the client credentials in the request-body.
Including the client credentials in the request-body using the two parameters is NOT RECOMMENDED
It is recommended to send client credentials as basic auth (encode credentials to base64 as add it to headers). Something like this:
curl -X POST \ http://localhost:8443/auth-service/oauth/token \ -H 'Authorization: Basic VEVTVF9TRVJWSUNFOnBhc3N3b3Jk' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d grant_type=client_credentials

Related

Authentication Required Vertx

The problem is when I write a simple query to this verticle I get this response.
Request:
curl -X GET http://localhost:8080/v1/calculation/DELPHI_METHOD_FORECAST
-H 'Content-Type: application/json'
-d '{"pessimisticMark":5,"optimisticMark":10}'
Response:
<html><head><meta http-equiv='refresh' content='1;url=/login?from=%2Fv1%2Fcalculation%2FDELPHI_METHOD_FORECAST'/><script>window.location.replace('/login?from=%2Fv1%2Fcalculation%2FDELPHI_METHOD_FORECAST');</script></head><body style='background-color:white; color:white;'>
Authentication required
<!--
-->
</body></html>
Verticle:
public class ExpertMarkCalculationController extends AbstractVerticle {
#Override
public void start() {
Router router = Router.router(vertx);
router.put("/v1/calculate/:methodName").handler(this::calculateForecast);
vertx.createHttpServer().requestHandler(router).listen(8080);
}
void calculateForecast(RoutingContext ctx) {
MethodType methodType = MethodType.valueOf(ctx.pathParam("methodName"));
JsonObject jsonMethodData = ctx.getBodyAsJson();
MethodData methodData = MethodDataParser.parseJsonToMethodData(jsonMethodData, methodType);
BasicExpertForecastCalculationService service = MethodMappingHandler.getForecastCalculationService(methodType);
service.process(methodData);
ctx.response().putHeader("content-type", "application/json").
end(MethodDataParser.parseMethodDataToJson(methodData, methodType));
}
}
As you can see there is no security configuration.

WCF: Matching a specific WS-Security scheme (Signature, Encrypt, UserPass)

I'm trying to match a specific WS-Security specification from a vendor. The following works in SOAP UI, which I am now trying to recreate in C# WCF (targeting .NET 4.7.2 or 4.8):
Username/Password element
Timestamp: 9000ms
Signature (cert A):
Sign with certificate
Prepend signature element
Sign timestamp and username token
Encryption (cert B):
Create encrypted key
Encrypt body, timestamp, and username token
Everything I've tried so far seems to encrypt everything in the WS-Security header except for the timestamp.
References followed:
WCF - Separate x509 for Signing and Encryption
SOAP UI Setup - Signature:
SOAP UI Setup - Encryption:
You can try to use IClientMessageInspectortor add a header to the message. In the BeforeSendRequest method, you can add a custom header to the outgoing message.Here is a demo:
public class CustomMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
return;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
request.Headers.Add(header);
return null;
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new CustomMessageInspector());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
Add CustContractBehaviorAttribute to apply it:

Adding User data to the JWT payload for spring security OAUth2

I am using a spring security OAuth2 using JWT tokens for some time but now I need to add 2 user defined values to the JWT token.
So when I added an additional set of parameters to the request /oauth/token?grant_type=client_credentials&user_value=1234567890.
The user_value above was for demonstrative purposes. I can trace it all the way into my CustomTokenEnhancer (I connected this as a way to pass this information all the way through). All the request parameters are visible through OAuth2Authentication authentication which is passed to my CustomTokenEnhancer.
Now I can add this information to the additional information which I see returned to me as part of the token request. See below.
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicGhpLWFwaSJdLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwib3JnYW5pemF0aW9uIjoicGhpLXVzZXJtZ3RuIiwidXNlcl90b2tlbiI6IjEyMzQ1Njc4OTAiLCJleHAiOjE0ODczNjc2NzEsImF1dGhvcml0aWVzIjpbIlJPTEVfQ0xJRU5UIl0sImp0aSI6IjFlZDMzZTAxLTc1ZGUtNDNjZC1hMzk2LTFkMzk2N2Y1NDQ5OCIsImNsaWVudF9pZCI6InBoaS11c2VyIn0.p628BNaaGljypEcGXZMkstNeTN-221qzzNQQ0npxDLTszWaXkgXqsBnBbKf9XMEtWTeCQkIszC9ne1Ei2X5IWTskhLT9Rko-8K7Jq-mXUc6HJZW-3tGV5rRer8Eyyw1wysW9Jiyp7sPkN-TIx12A70f_LHm6PrRR4ECppHWADs-2DvYA30p8omT1_RTt2WlqC40mopUN2TBPkb1WulVpOUEpcP358Ox8oVP8VQRSkLGZKB_b0KZAK9KGjLg6WNh8RghZaBuYuJQpITe_0XEBs_JfwrHhcK1IGaoYwSS7IGp3Cima9OMljdzayDKRqlfSl3WhaBuFmD1S37p-OVQL0A",
"token_type":"bearer",
"expires_in":8967,
"scope":"read write",
"user_value":"1234567890",
"jti":"1ed33e01-75de-43cd-a396-1d3967f54498"
}
But I don't want these values to be visible this way. I want them to be added to the encrypted token.
I spent some time looking and it isn't clear how i actually add that. This should be possible, shouldn't it?
Inside your own TokenEnhancer you have to encode it again:
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
// Generate additional Information [...]
// Write it to the token
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(addInfo);
// Encode Token to JWT
String encoded = super.encode(accessToken, authentication);
// Set JWT as value of the token
((DefaultOAuth2AccessToken) accessToken).setValue(encoded);
return accessToken;
}
You could solve this with the JwtHelper methods, but I just extended JwtAccessTokenConverter, so I could just use encode and decode.
When instantiating your Token enhancer, you have to add the keystore information:
private CustomTokenEnhancer jwtCustomEnhancer() {
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "***".toCharArray());
CustomTokenEnhancer converter = new CustomTokenEnhancer();
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
return converter;
}
I did something similar passing the value as a granted authority with help of user details service (not the token enhancer). At the client side, I wrote an extractor to retrieve the values from the principal injected by spring as type OAuth2Authentication. The following code is in Scala, but you may easily adapt to Java:
/**
* Mix-in to implicitly extract entity or identity from the principal.
*/
trait AuthorityExtractor {
def _contextName(implicit principal: OAuth2Authentication) = id(principal, "CONTEXT_")
def _entityId(implicit principal: OAuth2Authentication) = id(principal, "ENTITY_ID_")
def _userId(implicit principal: OAuth2Authentication) = id(principal, "USER_ID_")
def _identityId(implicit principal: OAuth2Authentication) = id(principal, "SELF_ID_")
private def id(principal: OAuth2Authentication, prefix: String) = {
import collection.JavaConversions._
principal
.getAuthorities
.filter(_.toString.startsWith(prefix))
.map(_.toString.substring(prefix.length))
.headOption.getOrElse("")
}
}
I extend JwtAccessTokenConverter class like that:
public class FooJwtAccessTokenConverter extends JwtAccessTokenConverter {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken fooAccessToken = new DefaultOAuth2AccessToken(accessToken);
fooAccessToken.getAdditionalInformation().put("foo_property", "foo");
return super.enhance(scaAccessToken, authentication);
}
In my AuthotizationServerConfig I create this:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
ScaJwtAccessTokenConverter accessTokenConverter = new ScaJwtAccessTokenConverter();
accessTokenConverter.setSigningKey("familia-mgpe"); // Parte da string de validação do token JWT.
return accessTokenConverter;
}

How to generate API key from .net core mvc service? (with Authentication in middleware)

I have a .NET core MVC rest service that restricts access to the API by validating a key.
It uses a middleware function that inspects the request for a valid key (before routing to the api function) and returns an unauthorized response if it is not valid.
My problem is how can I generate the key? Since all requests go through this middleware that checks if the key is valid, I need a way to generate the key.
I know it is common to have a separate identity server but I believe it is overkill for my situation, where a valid key gives access to the entire API. There is no permissions structure.
I could examine the incoming request for some indication that the user is trying to authenticate with a username+password and return the key, but this does not seem correct.
Is there a way I can allow a single service call to skip the middleware?
The middleware function:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PatrolLiveRestService.Middleware
{
public class KeyValidatorMiddleware
{
private readonly RequestDelegate _next;
//private IContactsRepository ContactsRepo { get; set; }
public KeyValidatorMiddleware(RequestDelegate next)//, IContactsRepository _repo)
{
_next = next;
//ContactsRepo = _repo;
}
public async Task Invoke(HttpContext context)
{
if (!context.Request.Headers.Keys.Contains("api-key"))
{
context.Response.StatusCode = 400; //Bad Request
await context.Response.WriteAsync("API Key is missing");
return;
}
if (!context.Request.Headers.Keys.Contains("device-id"))
{
context.Response.StatusCode = 400; //Bad Request
await context.Response.WriteAsync("Device ID is missing");
return;
}
string apiKey = context.Request.Headers["api-key"];
int deviceId;
if (!int.TryParse(context.Request.Headers["device-id"], out deviceId))
{
context.Response.StatusCode = 401; //UnAuthorized
await context.Response.WriteAsync("Invalid Device ID");
return;
}
if (!Common.ServiceCommon.IsKeyValid(apiKey, deviceId))
{
context.Response.StatusCode = 401; //UnAuthorized
await context.Response.WriteAsync("Invalid API Key");
return;
}
await _next.Invoke(context);
}
}
#region ExtensionMethod
public static class UserKeyValidatorsExtension
{
public static IApplicationBuilder ApplyUserKeyValidation(this IApplicationBuilder app)
{
app.UseMiddleware<KeyValidatorMiddleware>();
return app;
}
}
#endregion
}
Adding the middleware in Startup.cs Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.ApplyUserKeyValidation();
...
}
I see three ways around:
check context.Request.Url inside your KeyValidatorMiddleware (e.g. for path /no-need-for-api-key-endpoint don't force api-key header presence
alter your KeyValidatorMiddleware - instead of rejecting requests without api-key header, just set the context.User property to a ClaimsPrincipal with claims being device id and api key for valid incoming requests and filter out requests without valid authorization on controller level via [Authorize] attribute
do not implement it as middleware, implement it as action filter instead

Basic Authentication with Retrofit

I am trying to build a client for a REST API using Retrofit. The API uses basic auth and I have been unable to authenticate using Retrofit.
I tested the API using the curl below and it works as expected
curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{some_json}' -u api_key: https://apitest.com/api/v1/customers
Below is the Retrofit client
public interface UserService {
String HOST = "https://apitest.com";
public static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
public static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(HOST)
.addConverterFactory(GsonConverterFactory.create());
/*
* CREATE/UPDATE User
*/
#POST("api/v1/customers")
Call<UserAPIResponse> userUpdate(#Body UserUpdateRequest userUpdateRequest);
static UserService newInstance(String userAPIKey) {
String credentials = userAPIKey + ":";
final String basic = "Basic "+ Base64.encodeBase64(credentials.getBytes());
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", basic);
requestBuilder.header("Accept", "application/json");
requestBuilder.method(original.method(),original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(BlueshiftUserService.class);
}
When I call updateUser on the UserService
Response<UserAPIResponse> response = UserService.userUpdate(userUpdateRequest).execute();
The response.code is 401 (unauthorized/authentication failed)
The curl command with -u and the same credentials works as expected.
The issue was with the credentials encoding. I wasnt sending it as string.
byte[] encodedAuth= Base64.encodeBase64(credentials.getBytes());
final String basic = "Basic " + new String(encodedAuth);
use these libraries in Gradle file
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.cookpad.android.rxt4a:rxt4a:0.9.0'
compile 'io.reactivex:rxjava:1.0.12'
and put this classes in your project
public class ServiceGenerator {
private static final String TAG = erviceGenerator.class.getSimpleName();
public static final int READ_TIMEOUT = 10000;
public static final int CONNECT_TIMEOUT = 100000;
// No need to instantiate this class.
private ServiceGenerator(){}
public static <S> S createService(Class<S> serviceClass, String
endpoint) {
// Call basic auth generator method without user and pass
return createService(serviceClass, endpoint, null, null); }
public static <S> S createService(Class<S> serviceClass, String
endpoint, String username, String password) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
// Set endpoint url and use OkHTTP as HTTP client
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(endpoint)
.setConverter(new GsonConverter(new Gson()))
.setClient(new OkClient(okHttpClient));
if (username != null && password != null) {
// Concatenate username and password with colon for authentication
final String credentials = username + ":" + password;
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
// Create Base64 encoded string
String string = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", string);
request.addHeader("Accept", "application/json");
}
});
}
RestAdapter adapter = builder.build();
return adapter.create(serviceClass); } }
and this interface
public class TodolyClient {
private static final String TAG = TodolyClient.class.getSimpleName();
public static final String ENDPOINT = "your base URL";
public interface TodolyService {
#GET("/wp-json/wc/v2/products")(your remaining url)
Observable<Object> isAuthenticated();
}
}
and call the below method in your main activity
private void createProject() {
final TodolyClient.TodolyService service =ServiceGenerator.createService(
TodolyClient.TodolyService.class, TodolyClient.ENDPOINT, "your user name",
"your password");
Observable<Object> observable = service.isAuthenticated();
AndroidCompositeSubscription compositeSubscription = new AndroidCompositeSubscription();
observable
.lift(new OperatorAddToCompositeSubscription<Object>(compositeSubscription))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onNext(Object project) {
android.util.Log.d(TAG, "onNext: "+project.toString());
}
#Override
public void onCompleted() {
android.util.Log.d(TAG, "onNext:commm " );
}
#Override
public void onError(Throwable e) {
android.util.Log.d(TAG, "onNext: eeeeeeeee"+e.getMessage());
}
});
}
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class), You can add any character set for encoding as the third parameter here.
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/

Resources