Minimum spring boot configuration for OAuth2 server and client - spring-oauth2

I tried to code a 'Hello world' type of exercise to learn Spring Boot support for OAuth2, and the minimum required configuration.
Components:
Authorization server
webapp, which will call the auth server to authenticate the user, and will greet him
Expected flow:
I open the webapp
I get redirected to auth server
I login to auth server
I approve the requested scope
I get redirected back to webapp
I see a greeting (webapp should fetch my username from auth server)
The last point fails with 401:Could not obtain access token.
Last redirect link is http://localhost:9001/ui/login?code=wcXMG4&state=JEEYqC
Am I too naive to assume that the below code&configuration should suffice for my expected flow?
Auth server:
#SpringBootApplication
#EnableAuthorizationServer
#EnableResourceServer
#RestController
public class AuthServer {
public static void main(String[] args) {
SpringApplication.run(AuthServer.class);
}
#GetMapping("/whois")
Principal whois(Principal principal) {
return principal;
}
}
Auth server properties:
server.port=9000
server.contextPath=/sso
security.user.name=joe
security.user.password=password
security.oauth2.client.clientId=SOMEAPP
security.oauth2.client.clientSecret=SECRET
security.oauth2.client.authorized-grant-types=authorization_code,refresh_token,password
security.oauth2.client.scope=read
security.oauth2.resource.userInfoUri=http://localhost:9000/sso/whois
Webapp:
#SpringBootApplication
#EnableOAuth2Sso
#RestController
public class UiServer {
public static void main(String[] args) {
SpringApplication.run(UiServer.class);
}
#GetMapping("/")
String helloWorld(Principal principal) {
return "Yay, auth server provided identity, you are " + principal;
}
}
Webapp properties:
server.port=9001
server.contextPath=/ui
security.oauth2.client.client-id=SOMEAPP
security.oauth2.client.client-secret=SECRET
security.oauth2.client.accessTokenUri=http://localhost:9000/sso/oauth/access_token
security.oauth2.client.userAuthorizationUri=http://localhost:9000/sso/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:9000/sso/whois

After spinning up with debug on, it turns out that security.oauth2.client.accessTokenUri is incorrect.
The right endpoint is not .../oauth/access_token, but .../oauth/token.
Probably the tutorial I was looking at used outdated uri.
With that fix, this minimum config does what's expected of it, so I'll close the question.
The real fun begins when you try to customize it, bypassing the defaults; to me it seems that spring oauth still has major bugs, and requires hacky/unexpected approach to work around them in few use cases.

Related

Spring Boot Apache Kafka: ListenerExecutionFailedException Listener failed

Trying to read messages in consumer I get the following exception:
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener failed; nested exception is org.springframework.kafka.support.serializer.DeserializationException: failed to deserialize; nested exception is org.springframework.messaging.converter.MessageConversionException: failed to resolve class name. Class not found
...
Caused by: org.springframework.messaging.converter.MessageConversionException: failed to resolve class name. Class not found
I've been looking at the deserialiser but I cannot seem to find the right way to resolve it.
I am working on an application split across different microservices.
Right now I am working on the logic to send emails to newly registered users. So for this scenario, I have two microservices; the user service and the email service.
User Management - Producer - application.yml
kafka:
properties:
security.protocol: 'PLAINTEXT'
template:
default-topic: user-creation
producer:
bootstrap-servers: ${kafka_bootstrap_servers:localhost:9092}
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
key-serializer: org.apache.kafka.common.serialization.StringSerializer
Email service - Consumer - application.yml
kafka:
properties:
security.protocol: 'PLAINTEXT'
consumer:
bootstrap-servers: ${kafka_bootstrap_servers:localhost:9092}
group-id: user-creation-consumer
auto-offset-reset: earliest
# Configures the Spring Kafka ErrorHandlingDeserializer that delegates to the 'real' deserializers
key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
properties:
# Delegate deserializers
spring.json.trusted.packages: '*'
spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
The user management service uses a Kafka topic user-creation to alert different microservices of user generation.
private final KafkaTemplate<String, RegisteredUser> kafkaTemplate;
public void sendMessage(RegisteredUser registeredUser){
log.info("########## Sending message: {}", registeredUser.toString());
this.kafkaTemplate.send(new ProducerRecord<>("user-creation", registeredUser));
}
The email service listens to the for updates on the user-creation topic:
#KafkaListener(topics = "user-creation")
#Service
#Slf4j
public class Consumer {
#KafkaHandler
public void listen(String string){
log.info("Received String message {}", string);
}
#KafkaHandler
public void listen(ConsumerRecord<String, NewUser> record) {
log.info("Receive NewUser object {}", record.value());
}
#KafkaHandler(isDefault = true)
public void consume(#Payload Object data) {
log.info("received data='{}'", data);
}
}
The two services are split to avoid tight coupling; hence the object RegisteredUser DTO used in User creation is not accessible to the Email service or the other services. I am using a very similar class with the same signature and fields but that is still failing.
What is the best way to handle such a scenario? I am quite new to Kafka so I am not sure how to progress - most tutorials online have the producer and consumer in the same code base so the DTO can be easily shared.
The idea is that the RegisteredUser DTO has fields/elements useful for other services so it will include more data - I only need to read a part of it.
TIA

Remove Default Authorization Header. ( Spring Webflux Security)

I am using spring security with the below configurations.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()
.csrf().disable()
.logout().disable();
return http.build();
}
And my ShipxpressReactiveSecurityContextRepository load method looks like below.
#Override
public Mono<SecurityContext> load(ServerWebExchange serverWebExchange) {
String authorization = CollectionToolkit.getFirstElement(
serverWebExchange.getRequest().getHeaders().get(ShipxSecurityConstant.Header.AUTHORIZATION_HEADER));
if (StringToolkit.isNotEmpty(authorization)) {
return authenticate(authorization, serverWebExchange);
} else {
return Mono.empty();
}
}
My use case is properly working. but my issue is here when I try to access my API from the browser (ex : localhost:8180/dmu) Spring browser prompt to authentication. in that situation if i enter the wrong user / password i cant change it with next request.
Because There is a "Authorization" request header with invalid authentication.
I have two questions.
How we can remove default request headers from the browser ( access get methods from browser URL)
how i can disable to generate default request headers from spring.
Example screenshot:

.NET CORE 3.1 Razor Page calling API returns nothing on IIS Server returns data locally

I have a C# .Net Core 3.1 Razor Page where I am calling a 3rd Party API. When I run it locally in debug it returns a full JSON and is parsed into a strongly typed class successfully, but on the server hosting within IIS in process it fails with a 500 error that looks like an empty JSON object
Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line 0
I am using an AJAX call to a Razor handler method. The website is using oAuth2 to authenticate the user (this is an intranet application) and this part works as expected both locally and on the server. My suspicion is that the user is "authenticated" locally (through windows or some other method and that data is being sent to the API) but on the server the Claims are validated but not passed to the API. I have used Postman on the server to validate the API can be reached. I have also contacted the API host and verified the request is not failing from the API, its not even reaching it. It requires HTTPS on port 443 which I confirmed is open between the server and the API.
Here is the Razor page method (called from AJAX).
public async Task<JsonResult> OnGetPerson()
{
var userInfo = await whitePages.GetPersonByIDAsync(ID);
return new JsonResult(userInfo);
}
This calls a function within an Interface
public async Task<Models.PeopleAPI> GetPersonByIDAsync(string id)
{
string uriString = createGetPersonByidUriString(id); //This just builds the https string
var response = await _httpClient.GetAsync(uriString);
List<Models.PeopleAPI> people=JsonConvert.DeserializeObject<List<Models.PeopleAPI(await response.Content.ReadAsStringAsync());
return people.Single(); // use Single to guarantee only one result was receieved, otherwise throw an exception
}
My peopleAPI model is as follows.
public class PeopleAPI
{
public int PersonID { get; set; }
public string Domain { get; set; }
public string NTID { get; set; }
public string EmployeeID { get; set; }
public string UnixID { get; set; }
etc
etc
}
Please help, I have tried adding in user credentials to make the API call, various other Stack suggestions (5 to 10 different approaches, sync, async, etc) and just cannot seem to come to a solution (each one works locally, but not on the IIS server).
HELP!!!
I was able to solve my own issue using the visual studio remote debugging on my server. It turns out the local debug on my machine was not using the proxy server (System.Net.Http.HttpWindowsProxy was showing empty) but on the server it was using a different version (system.Net.Http.HttpEnvironmentProxy) and using the default proxy specified within netsh winhttp proxy. Once I set the startup.cs to specify the AddHttpClient I also included the useProxy = false. This was within an intranet and hope maybe someone else can use this in the future.
services.AddHttpClient("coolapi", c =>
{
c.BaseAddress = new Uri("https://something.com/");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
UseProxy = false
}) ;

Javamail fail with an authentication Exception, why?

I'm working in a Java EE webApplication and I hope to send emails to my clients.
So I add mail.jar and activation.jar and I tried this simple code:
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendHTML {
static Properties mailServerProperties;
static Session getMailSession;
static MimeMessage generateMailMessage;
public static void main(String args[]) throws AddressException, MessagingException {
generateAndSendEmail();
System.out.println("\n\n ===> Your Java Program has just sent an Email successfully. Check your email..");
}
public static void generateAndSendEmail() throws AddressException, MessagingException {
//Step1
System.out.println("\n 1st ===> setup Mail Server Properties..");
mailServerProperties = System.getProperties();
mailServerProperties.put("mail.smtp.port", "587"); // TLS Port
mailServerProperties.put("mail.smtp.auth", "true"); // Enable Authentication
mailServerProperties.put("mail.smtp.starttls.enable", "true"); // Enable StartTLS
System.out.println("Mail Server Properties have been setup successfully..");
//Step2
System.out.println("\n\n 2nd ===> get Mail Session..");
getMailSession = Session.getDefaultInstance(mailServerProperties, null);
generateMailMessage = new MimeMessage(getMailSession);
generateMailMessage.addRecipient(Message.RecipientType.TO, new InternetAddress("Recipient#gmail.com"));
generateMailMessage.addRecipient(Message.RecipientType.CC, new InternetAddress("Recipient#gmail.com"));
generateMailMessage.setSubject("Greetings from me..");
String emailBody = "Test email by me JavaMail API example. " + "<br><br> Regards, <br>Admin";
generateMailMessage.setContent(emailBody, "text/html");
System.out.println("Mail Session has been created successfully..");
//Step3
System.out.println("\n\n 3rd ===> Get Session and Send mail");
Transport transport = getMailSession.getTransport("smtp");
// Enter your correct gmail UserID and Password
transport.connect("smtp.gmail.com", "myusername#gmail.com", "mypassword");
transport.sendMessage(generateMailMessage, generateMailMessage.getAllRecipients());
transport.close();
}
}
So Step1 and Step2 work successfully, but Step3 generate an Authentication exception:
1st ===> setup Mail Server Properties..
Mail Server Properties have been setup successfully..
2nd ===> get Mail Session..
Mail Session has been created successfully..
3rd ===> Get Session and Send mail
Exception in thread "main" javax.mail.AuthenticationFailedException
at javax.mail.Service.connect(Service.java:264)
at javax.mail.Service.connect(Service.java:134)
at entity.SendHTML.generateAndSendEmail(SendHTML.java:53)
at entity.SendHTML.main(SendHTML.java:24)
Please can someone help me to fixe the problem ?
First, there is nothing named "JEE", you meant "Java EE".
If you're using Java EE, you shouldn't need mail.jar and activation.jar, they're already part of every Java EE server.
You're going to want to read these JavaMail FAQ entries on common mistakes and how to debug JavaMail applications.
AuthenticationFailedException means the server doesn't like your username and password.
You're probably running into the problem described here.

NServiceBus without input queue

Is it possible to use NServiceBus in an application without having any input queues?
Reason is, I have an ASP.NET MVC application that sends messages to other applications when something happens (e.g. a new user registers). The web application never recieves any responses or other messages and therefore I would like not to bog the app. with the msmq peeking which throws an exception every second.
That is supported, just remove the msmstranport config section and all should be fine. This works against 2.0.1281.0 (net4) version of NServiceBus with no app.config present
using NServiceBus;
namespace SendOnlyEndpoint.Custom
{
public class Program
{
static void Main(string[] args)
{
var bus = Configure.With()
.DefaultBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.CreateBus()
.Start();
bus.Send("SendOnlyDestination",new TestMessage());
}
}
public class TestMessage : IMessage
{
}
}
More info on send only endpoints here
I would try not configuring an input queue. Bus.Send will use an internal outbound queue to send messages.