ASP.NET Core Windows Authentication using Min APIs

How to use Windows Authentication for ASP.NET Core Web APIs? We will try to do something like that in today’s tutorial using Min APIs.

First, create a new empty asp.net web project using the below command:
dotnet new web -o minwinauth

Then, switch to the minwinauth folder and add Negotiate package to the project since Windows Authentication requires the library:
dotnet add package Microsoft.AspNetCore.Authentication.Negotiate

Open your project using VS Code / any of your favourite IDE and paste the code below to the Program.cs:

using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// To have Windows Authentication, we need to add NegotiateDefaults.AuthenticationScheme
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/", () => "Hello World!");
// Authorize attribute make the API needs authentication, and Negotiate... is for Windows Authentication.
app.MapGet("/admin", [Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)] (HttpContext context) =>
    "The /admin endpoint is for authorizers only. User ID: " + context.User.Identity?.Name);

app.Run();

Run the project using the command below:
dotnet run

If you browse the site, you will get “Hello World!” without you keying in any credential due to root directory is don’t need authentication. But if you browse /admin, a login dialog box will appear, and you need to key in the Windows credential.

If you click cancel or key in a wrong credential, you will get 401 Unauthorized with authentication method is Negotiate.

But if you key in correct Windows credential, you will able to login and see the message like below:
The /admin endpoint is for authorizers only. User ID: xxx/yyy

PS:
Please note this working in Windows environment only, if you try in Linux environment, you most probably will get 401 unauthorized due to no authentication can be made. If you want Linux to support this Windows Authentication, you need to setup the Linux environment configuration as stated in ASP.NET Core documentation https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-6.0&tabs=visual-studio

How to check where dotnet installed in Linux/Ubuntu/Pi?

How do we know what is the location of dotnet installed in Linux/Ubuntu/Raspberry Pi? It is quite simple, just run the which command in Bash terminal to get the path of dotnet location.

which dotnet

# Possible Result 1
/usr/bin/dotnet
# Possible Result 2 (ubuntu here is user folder)
/home/ubuntu/dotnet/dotnet

Result 1 normally is we have installed the dotnet using installer and result 2 is normally we use binaries to install the dotnet.

What is the purpose of Partial class in DBContext and how to add own logic to model classes?

If you try scaffolding .NET Entity Framework Core using Database First approach, you will find the DBContext and the model classes are normally in partial class. What is the reason and purpose for these classes to put in partial class?

One of the main reasons is to allow you add your own logic to the DBContext and the model classes. You should know, every time you re-scaffold the tables, EF Tool or Visual Studio will overwrite all the DBContext code and the model classes code. By using the partial class, you can add your own logic using another file, like this every time you re-scaffold the tables, no overwritten issues will happen since your own code is on your own file instead of the files that need to re-scaffold.

A simple example below which Customer.cs is scaffolded by EF Tool, if you want to add a new own field, you can use the same class name and put in to another file e.g. Customer_UserCode.cs. Like this, next time you re-scaffold the tables, FullName for the Customer model will still be there since the Customer_UserCode.cs will not be overwritten by EF Tool.

// Filename: Customer.cs
public partial class Customer
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
}

// Filename: Customer_UserCode.cs
public partial class Customer 
{
	public string FullName { get { return FirstName + " " + LastName; } }
}

How to get / show SQL ‘Print’ messages in Entity Framework?

If you call a stored procedure for SQL Server using Entity Framework / Core, sometimes it contains the SQL Print statements, the EF Core by default just will not get the message back to your program. To show it in your program, you can use the SqlConnection to get the messages.

((SqlConnection)_dbContext.Database.GetDbConnection()).InfoMessage += (s, e) => Console.WriteLine(e.Message);

Like this, it will show on your console for any print messages.

Install Docker on Red Hat Enterprise Linux (RHEL) 8

To install Docker on RHEL, the first thing you need to check is what is your CPU architecture for your Red Hat OS. Type the command below if you are not sure of the architecture.

uname -m

If you see s309x, then you can proceed to install steps as written in Docker official RHEL installation documentation > Install Docker Engine on RHEL.

How about if you see other than s309x, for example most common one like x86_64, then you cannot install using the official documentation above. If you proceed, you will hit the error similar to below:

sudo yum install docker-ce docker-ce-cli containerd.io
Updating Subscription Management repositories.
Docker CE Stable - x86_64 310 B/s | 381 B 00:01
Errors during downloading metadata for repository 'docker-ce-stable':
- Status code: 404 for https://download.docker.com/linux/rhel/8/x86_64/stable/repodata/repomd.xml (IP: xx.xx.xx.xx)
Error: Failed to download metadata for repo 'docker-ce-stable': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried

How to resolve this issue? You cannot due to the Docker packages are only for RHEL on s390x (IBM Z).

Luckily, in the same installation document, it has written there you might able to install the CentOS package on RHEL.

We currently only provide packages for RHEL on s390x (IBM Z). Other architectures are not yet supported for RHEL, but you may be able to install the CentOS packages on RHEL. Refer to the Install Docker Engine on CentOS page for details.

Therefore, just proceed to Install Docker Engine on CentOS and you should able to install the Docker on RHEL. It works on RHEL 8.5 for my scenario.

Serve static files using Nginx and reverse proxy API

How can we serve static files using Nginx and serve API for .NET/Java/Node.js… at the same time in Linux?

First, we go to the Nginx sites-available folder and copy the default configuration.

sudo cp default example.com

Edit the example.com file to something like below:

server {
        # Use port 81 for this example
        listen 81 default_server;
        listen [::]:81 default_server;

        # Serve the static file from the folder below
        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then to dotnet.
                try_files $uri $uri/ @dotnet;
        }

        # The .NET Kestrel host at port 5000
        location @dotnet {
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://localhost:5000;
        }
}

Then, we need to enable it for site-enabled

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Finally, reload configuration / restart the Nginx and your new server is up :).

sudo systemctl reload nginx

ERR_TOO_MANY_REDIRECTS OKTA Authentication in Spring Boot

You are trying to develop an OKTA login authentication in Java Spring Boot, but you get ERR_TOO_MANY_REDIRECTS error as like below:

This page isn’t working right now
localhost redirected you too many times.
To fix this issue, try clearing your cookies.
ERR_TOO_MANY_REDIRECTS

How to resolve this issue? First to check if this is due to Sign-in redirect URIs or not, we can try set the URI to default listening URI for OKTA code http://localhost:8080/login/oauth2/code/okta. Go to OKTA Admin Dashboard site > Applications > Applications and change the URI to default listening URI

Change the application.properties redirect URI to as below:

okta.oauth2.redirect-uri=/login/oauth2/code/okta

Run the Spring Boot application, if you can login successfully, then it should be some configuration or code issue.

We change back the redirect URI to original URI

okta.oauth2.redirect-uri=/authorization-code/callback

From the OKTA documentation, found this needs to be added:

package com.example.oktademo;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // all routes protected
        http.authorizeRequests()
                .anyRequest().authenticated()
                // enable OAuth2/OIDC
                .and()
                .oauth2Login();
    }
}

Run the Spring Boot app again, you should be able to login without error.

Reference:
https://www.baeldung.com/spring-security-okta
https://developer.okta.com/docs/guides/protect-your-api/springboot/main/#require-authorization-for-everything
https://devforum.okta.com/t/setting-up-okta-oidc-for-use-with-spring-boot-2-3-1-release/14490
https://stackoverflow.com/questions/58110915/too-many-redirects-okta-authentication

Consuming a RESTful Web Service in Spring Boot using RestTemplate

How to consume a RESTful Web Service in Spring Boot? If you have checked spring.io, they have a guide on how to use it but unfortunately, the API is unable to access during I tried the example. Therefore, this blog post will use an alternative quote API using a similar approach.

This tutorial will build an application by using Spring’s RestTemplate to retrieve a random quote from https://api.quotable.io/random. We will use Visual Studio Code as our text editor, JDK 1.8 or later, and Maven. Please make sure Java has been setup correctly, then Extension Pack for Java and Spring Initializr Java Support extensions for VS code have been installed too (the publisher of the extensions is Microsoft).

Press Ctrl+Alt+P to run the VS Code Command Palette:

  1. Type Spring Initializr and then select Spring Initializr: Create a Maven Project…
  2. Specify Spring Boot version, normally default will do
  3. Specify project language: Java
  4. Group Id: com.example.consumingrest
  5. Artifact Id: ConsumingRest
  6. Packaging type: Jar
  7. Java version: 8+
  8. Dependencies: Spring Web
  9. Lastly, select a destination folder you want to project file extract to.

We will get the RESTful service by using https://api.quotable.io/random and it can be browsed using a web browser. The JSON document will look something like this:

{
    "_id": "w0fjvau7GwbH",
    "tags": [
        "famous-quotes"
    ],
    "content": "All is flux; nothing stays still.",
    "author": "Heraclitus",
    "authorSlug": "heraclitus",
    "length": 33,
    "dateAdded": "2019-10-12",
    "dateModified": "2019-10-12"
}

We will create a domain class (src/main/java/com/example/consumingrest/Quote.java) that contains the fields for the JSON object.

package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Quote {
    @JsonProperty("_id")
    private String _id;
    @JsonProperty("content")
    private String content;
    @JsonProperty("author")
    private String author;
    @JsonProperty("authorSlug")
    private String authorSlug;
    @JsonProperty("length")
    private int length;
    @JsonProperty("tags")
    private String[] tags;

    public Quote() {
    }

    public String get_Id() {
        return _id;
    }

    public void set_Id(String _id) {
        this._id = _id;
    }

    public String get_Content() {
        return content;
    }

    public void set_Content(String content) {
        this.content = content;
    }

    public String get_Author() {
        return author;
    }

    public void set_Author(String author) {
        this.author = author;
    }

    public String get_AuthorSlug() {
        return authorSlug;
    }

    public void set_AuthorSlug(String authorSlug) {
        this.authorSlug = authorSlug;
    }

    public int get_Length() {
        return length;
    }

    public void set_Length(int length) {
        this.length = length;
    }

    public String[] get_Tags() {
        return tags;
    }

    public void set_Tags(String[] tags) {
        this.tags = tags;
    }

    @Override
    public String toString() {
        return "@Value {" +
            "_id=" + _id +
            ", content=" + content +
            ", author=" + author +
            ", authorSlug=" + authorSlug +
            ", length=" + length +
            ", tags=" + tagsToString() + '\'' +
            "}";
    }

    private String tagsToString() {
        StringBuilder sb = new StringBuilder();
        for (String string : tags) {
            sb.append(string + ", ");
        }
        return sb.substring(0, sb.length() - 2);
    }
}

Then, we will use a RestTemplate as a synchronized web client to perform requests to the API and it will use the Jackson JSON processing library to process the API Data.

// src/main/java/com/example/consumingrest/ConsumingRestApplication.java
package com.example.consumingrest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumingRestApplication {
	// Logger to output result to console
	private static final Logger log = LoggerFactory.getLogger(ConsumingRestApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(ConsumingRestApplication.class, args);
	}

	@Bean
	public RestTemplate restTemplate(RestTemplateBuilder builder) {
		return builder.build();
	}

	@Bean
	public CommandLineRunner run(RestTemplate restTemplate) throws Exception { // Runs the RestTemplate
		return args -> {
			Quote quote = restTemplate.getForObject("https://api.quotable.io/random", Quote.class);
			log.info(quote.toString());
		};
	}

}

Finally, just hit Run Java / Debug Java using the Run or Debug button on the top right of VS Code. Alternatively, you can use this command ./mvnw spring-boot:run to execute it without debug function.

The sample result is as below:

2022-02-13 12:07:54.979  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : No active profile set, falling back to default profiles: default
2022-02-13 12:07:57.873  INFO 11731 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-02-13 12:07:57.908  INFO 11731 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-02-13 12:07:57.909  INFO 11731 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-02-13 12:07:58.130  INFO 11731 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-02-13 12:07:58.131  INFO 11731 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2958 ms
2022-02-13 12:07:59.317  INFO 11731 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-02-13 12:07:59.349  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : Started ConsumingRestApplication in 5.661 seconds (JVM running for 6.724)
2022-02-13 12:08:01.327  INFO 11731 --- [           main] c.e.c.ConsumingRestApplication           : @Value {_id=eDIMF-N3ATJ3, content=Let your hook always be cast; in the pool where you least expect it, there will be a fish., author=Ovid, authorSlug=ovid, length=90, tags=famous-quotes'}

Reference:
https://spring.io/guides/gs/consuming-rest/
https://github.com/lukePeavey/quotable#get-random-quote

Sample project repository:
https://github.com/sanme98/gs-consuming-rest

[.NET Core] System.Net.Sockets.SocketException (10013): An attempt was made to access a socket in a way forbidden by its access permissions.

If you get the error message below during you start a .NET Core web application or run the web app using Visual Studio Kestrel for this line of code CreateHostBuilder(args).Build().Run();, quite likely it was due to the same port number have been bound to another application.

System.Net.Sockets.SocketException (10013): An attempt was made to access a socket in a way forbidden by its access permissions.
   at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, String callerName)
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.<>c__DisplayClass21_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()

The possible solution can be to turn off the application that currently occupies the same port or you can change the .NET Core Web application port using launchSettings.json > applicationUrl (it located inside Properties folder if you are using Visual Studio).

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:53627",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "TestSocketException": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5001",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Different Types of useEffect in React Hooks

If you are using React Hooks, you will find useEffect is a quite common keyword you will see in the JSX code. However, many are not clear that useEffect has different types of usage and different types actually do different things. As per definition from the official documentation,

The Effect Hook lets you perform side effects in function components

So, what are the side effects? Before we start to explain, let’s see the anatomy of useEffect, and then we go to the four different types of useEffect.

useEffect(() => {   
  return () => {
    // Return is optional
  }
}, [Dependencies]); 
// Dependencies is optional and it can be an empty array [] also

1. Run only one time on component mount

useEffect(() => {

}, []); // An empty array []

This is the first type of the useEffect in React is it runs only one time on the component mount. It is like componentDidMount in classic React or Page_Load in .NET WebForm.

2. Run on every update

useEffect(() => {  
  
}); // Without any dependencies or an empty array []

The second type is it will run on every component update such as state changes. It is similar to componentDidUpdate in classic React.

3. Run if the dependency changes

useEffect(() => {  

}, [Dependency1, Dependency2]); // Dependencies

The third type is similar to the second type above, just its limits to the dependencies inside the array we specify. Please note the dependency can be one or more, it just needs to be separated with a common.

4. Run on unmount

useEffect(() => {  
  return () => {
    //Run on unmount
  }
}, []);

The last type is it runs during component unmount. It is similar to componentWillUnmount in classic React or Page_Unload on .NET WebForm.

Hopefully, with these four different types of useEffect explanation, we will be clearer on how to use useEffect correctly.