• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Careers

  • Blog

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    VisionPort at University of Tokyo, New York office: An Exhibition for Peace on August 6th and 7th

    Samuel Stern

    By Samuel Stern
    July 26, 2022

    3D visualization of Hiroshima with photos pinned throughout
    Ground Zero, Hiroshima, Japan – August 6th, 1945. Visualized by the lab of Professor Hidenori Watanave.

    Technology and education go hand in hand, and the VisionPort platform is being used every day to make that connection.

    We are extremely honored to be able to contribute to the first exhibition at the University of Tokyo’s New York office, “Convergence of Peace Activities: Connecting and Integrating by Technologies”.

    It is said that those who do not learn from history are condemned to repeat it, and in that vein, the exhibition, drawing from the work of Professor Hidenori Watanave, will be using the VisionPort platform to educate viewers on the realities of the bombings of Hiroshima and Nagasaki, on the date of the 77th anniversary of the first nuclear weapon used in war.

    Several women and men in a presentation on the VisionPort

    The team has been collecting and colorizing photographic material from the aftermath of the bombings for over 10 years. The exhibition will combine that work with interviews and writings from survivors on a GIS canvas to allow attendees to see what it looked like and to hear from those who were there.

    The lab will also be presenting the work they have been doing covering the ongoing conflict in Ukraine. Day by day they collect the latest images from the war, identify the locations of the events and use geospatial data to map and present it in an interactive, 3D environment.

    A 3D rendering of bombed buildings in Ukraine
    Invasion of Ukraine, provided by the labs of Professors Hidenori Watanave and Taichi Furuhashi.

    The exhibition will serve to show us where we have been and where we are now, in hopes of being a “convergence,” a place to connect and use all of our available information and technologies so that we may begin a new era of understanding and in turn, peace.

    The exhibition will also feature work done by Co-Op Peace Map, Mainichi Newspaper and the University of Kyoto.

    The educational work that the University of Tokyo is doing with the VisionPort platform is inspiring and we look forward to being there to see it in memoriam of that fateful day.

    Register here and join us for this viewing on August 6th and 7th at the University of Tokyo’s New York office located at 145 W. 57th St., 21st Floor, New York, NY 10019.

    For more information about VisionPort, email sales@visionport.com or visit visionport.com.

    Images and photography provided by the University of Tokyo, the department of Interfaculty Initiative in Information Studies, the lab of Professor Hidenori Watanave, and the lab of Professor Taichi Furuhashi.


    visionport event education

    Windows SSH key agent forwarding confirmation

    Ron Phipps

    By Ron Phipps
    July 26, 2022

    A sunset with silhouetted construction equipment

    At End Point we use SSH keys extensively, primarily for authentication with servers for remote shell access as well as with Git services including GitHub, GitLab, and Bitbucket. Most of the time the servers we are attempting to reach are blocked from direct access and require that we go through an intermediate “jump server”.

    Because of this need to jump from server to server we utilize SSH key forwarding that allows us to use the private key stored on our local system to authenticate with each of the servers in the chain. When we reach our destination server we can use the same private key to authenticate with the Git hosting service and perform git commands without having to enter a password.

    One of the best practices when using SSH key forwarding is to use an option called key confirmation. When key confirmation is turned on, each time a request is made to use the private key that is loaded in the SSH agent a prompt will appear on your local machine to approve the use of the key. This reduces the ability for an attacker to use your private key without approval.

    For the longest time SSH key confirmation was not available on Windows. One of the most popular SSH clients on Windows is PuTTY and its agent (pageant) does not support the option. Many other SSH key compatible Windows applications use PuTTY’s agent for SSH key caching and as a result these applications also lack the ability for key confirmation.

    KeePass and KeeAgent

    To use key confirmation on Windows we need to utilize two programs, KeePass and KeeAgent. KeePass is an open source password manager and KeeAgent is a plugin for KeePass that provides a PuTTY-compatible SSH key agent. It also appears that KeeAgent has support for integration with the Windows shell, Cygwin/MSYS, and Git Bash.

    The instructions below will assume that you already have a SSH key in PuTTY that you’d like to use with key confirmation and that you have previously used PuTTY with key forwarding.

    You should start by installing KeePass. Then install KeeAgent.

    Once both are installed create a new KeePass database, or use your existing database if you are already a KeePass user.

    Add a new entry to the database and name it SSH key. Enter your SSH key password into the Password and Repeat fields.

    KeePass’s Add Entry screen

    Then click on the KeeAgent tab and check ‘Allow KeeAgent to use this entry’.

    In the Private Key section select External File and point it at your PuTTY private key. If you have entered the correct password on the first tab you should see your key comment and fingerprint listed. Then press OK.

    The KeeAgent tab in Add Entry

    Verify that confirmation is enabled by clicking on Tools -> Options and selecting the KeeAgent tab.

    A checked box reading “Always require user confirmation when a client program requests to use a key”

    Press OK.

    Then go to File -> Save. Close KeePass and re-open it. You’ll be asked to enter your KeePass password and then you can verify that the agent is loaded with your key by clicking Tools -> KeeAgent.

    KeeAgent in Agent Mode

    Now when we use PuTTY or another PuTTY agent-compatible program we’ll be presented with a confirmation dialog. Clicking Yes will allow the key to be used.

    KeeAgent’s confirmation dialog

    Notice that the default selected option is No. This is different than the standard openssh-askpass on Linux, which defaults to Yes. If you’re typing along in a fury and the confirmation window pops up and you hit Enter or space, it will decline the use of your SSH key, rather than accepting it.

    If you have enabled SSH key forwarding in the PuTTY options for the connection you’ll be using you can then SSH to other servers using the same key and each time you do so the confirmation will be presented to you.

    If you close KeePass the key agent will be closed and unavailable for future connections. Re-opening KeePass will allow the key to be used again.

    If you use Windows and SSH agent forwarding but have never tried agent confirmation to protect against malicious use of your secret key, give KeePass and KeeAgent a try!


    windows ssh

    CSTE Conference EpiTrax retrospective

    Steve Yoman

    By Steve Yoman
    June 29, 2022

    Banner photo of 4 End Pointers in our conference booth

    Last week we were in Louisville, Kentucky for the CSTE Conference. End Point staffed a conference booth to represent the EpiTrax public health surveillance system to a wonderful group of public health experts.

    You can read some background about the conference and CSTE in our earlier blog post announcing our plans.

    Photo of attendees at a CSTE conference session

    We really enjoyed meeting new friends in person after two years of canceled events due to the pandemic. We spoke with staff from health departments and disease surveillance teams from several state and local jurisdictions, as well as with experts from the CDC and other software and service vendors.

    One of the highlights was going around to meet other people staffing booths at the conference. It charged us up to see and hear about all of the interesting and innovative things going on in the public health space at a time when there is so much that needs to be done. We were particularly struck by the efforts being made in onboarding and distributing ELRs and eCRs, areas where the Electronic Message Staging Area (EMSA, which we deploy and support) can complement and enrich those activities.

    Photo of CSTE conference hall

    The open-source disease surveillance and reporting software EMSA and EpiTrax both enjoyed well-deserved attention as we demonstrated them to numerous groups seeking better solutions to serve people in their jurisdictions. People were very interested in the functionality EpiTrax has for case management, encounters, and NMI reporting.

    Of course there is a lot more we could say about EpiTrax and EMSA. So, if you didn’t find us at the conference or if you are interested in what EpiTrax and EMSA can do, contact us here online. We are happy to give you a demo!


    conference epitrax emsa

    How to deploy a Django App with Aurora Serverless and AWS Copilot

    Jeffry Johar

    By Jeffry Johar
    June 26, 2022

    Photo of an aurora
    Photo by Виктор Куликов

    AWS Copilot has the capability to provision an external database for its containerized work load. The database options are DynamoDB (NoSQL), Aurora Serverless (SQL), and S3 Buckets. For this blog we are going to provision and use Aurora Serverless with a containerized Django app. Aurora Serverless comes with 2 options for its engine: MySQL or PostgreSQL.

    Watch Amazon’s 2-minute introduction video to get the basic idea of Aurora Serverless.

    We are going to work with the same Django application from my last article on AWS Copilot.

    In my last article, the Django application was deployed with SQLite as the database. The application’s data is stored in SQLite which resides internally inside the container. The problem with this setup is the data is not persistent. Whenever we redeploy the application, the container will get a new filesystem. Thus all old data will be removed automatically.

    Now we are moving away the application’s data externally so that the life of the data does not depend on the container. We are going to put the data on the Aurora Serverless with PostgreSQL as the engine.

    Diagram of Django app with SQLite database

    Django with SQLite as the internal database


    Diagram of Django app with AWS Aurora database

    Django with Aurora Serverless as the external database


    The Prerequisites

    Docker, AWS CLI, and AWS Copilot CLI are required. Please refer to my last article for how to install them.

    The Django Project

    Create a Django project by using a Python Docker Image. You can clone my Git project to get the Dockerfile, docker-compose.yaml and requirements.txt that I’m using:

    $ git clone https://github.com/aburayyanjeffry/django-copilot.git django-aurora
    

    Go to the django-aurora directory and execute docker-compose to create a Django project named “mydjango”.

    $ cd django-aurora
    $ docker-compose run web django-admin startproject mydjango
    

    The Deployment with AWS Copilot

    Execute the following command to create a AWS Copilot application with the name of “mydjango”, a load balancer container with the service name “django-web” which is made from the Dockerfile in the current directory.

    $ copilot init \
    -a mydjango \
    -t "Load Balanced Web Service" -n django-web \
    -d ./Dockerfile
    

    Answer N to the following question. We want to defer the deployment until we have set up the database.

    All right, you're all set for local development.
    
     Would you like to deploy a test environment? [? for help] (y/N) N
    

    We need to create an environment for our application. Execute the following to create an environment named test for the “mydjango” application with the default configuration.

    $ copilot env init \
    --name test \
    --profile default \
    --app mydjango \
    --default-config
    

    Now we are going to generate a config for our Aurora Serverless database. Basically this is the CloudFormation template that will be used for Aurora Serverless.

    Execute the following to generate the configuration for an Aurora cluster named “mydjango-db” that we will use for the “django-web” application. The Aurora cluster will be using the PostgreSQL engine and the database name will be “mydb”.

    $ copilot storage init \
    -n mydjango-db \
    -t Aurora -w \
    django-web \
    --engine PostgreSQL \
    --initial-db mydb
    

    Take note of the injected environment variable name. This is where the database info and credentials are stored, and we will use this variable in later steps.

    ✔ Wrote CloudFormation template at copilot/django-web/addons/mydjango-db.yml
    
    Recommended follow-up actions:
     - Update django-web's code to leverage the injected environment variable MYDJANGODB_SECRET.
    

    Edit mydjango/settings.py to include the following. We will pass the injected environment variable we got previously to the function for getting the DBINFO variables.

    from pathlib import Path
    import os
    import json
    ...
    ALLOWED_HOSTS = ['*']
    ...
    DBINFO = json.loads(os.environ.get('MYDJANGODB_SECRET', '{}'))
    DATABASES = {
       'default': {
           'ENGINE': 'django.db.backends.postgresql',
           'HOST': DBINFO['host'],
           'PORT': DBINFO['port'],
           'NAME': DBINFO['dbname'],
           'USER': DBINFO['username'],
           'PASSWORD': DBINFO['password'],
       }
    }
    

    Deploy the application:

    $ copilot deploy --name django-web
    

    Open the terminal of the service:

    $ copilot svc exec
    

    Execute the following commands to migrate the initial database and to create a superuser account:

    $ python manage.py migrate
    $ python manage.py createsuperuser
    

    Execute the following command to check on the environment variable. Take note of the MYDJANGODB_SECRET variable. It is the variable that holds the database information.

    $ env | grep MYDJANGODB_SECRET
    

    How to query Aurora Serverless

    We can use the Query Editor at AWS Console for RDS to query Aurora Serverless.

    Click the DB base on the DB identifier from the injected environment variable and click Modify.

    Screenshot of Amazon RDS main control panel

    Click the check box for Data API.

    Screenshot of Amazon RDS Web Service Data API checkbox

    Select Apply Immediately.

    Screenshot of Amazon RDS Apply Immediately option

    Click Query Editor and fill in the Database information from the injected environment variable.

    Screenshot showing environment variable data extracted into the AWS RDS connection setup panel

    Now you may use the Query Editor to query the database. Execute the following query to list all tables in the database:

    Screenshot of Amazon RDS Query Editor and results

    The End

    That’s all, folks. We have deployed a containerized Django application and an Aurora Serverless with AWS Copilot. For further info on AWS Copilot visit its website.


    docker containers aws postgres

    How to deploy a containerized Django app with AWS Copilot

    Jeffry Johar

    By Jeffry Johar
    June 21, 2022

    Photo of 2 pilots in an airplane cockpit

    Generally there are 2 major options at AWS when it comes to deployment of containerized applications. You can either go for EKS or ECS.

    EKS (Elastic Kubernetes Service) is the managed Kubernetes service by AWS. ECS (Elastic Container Service), on the other hand, is AWS’s own way to manage your containerized application. You can learn more about EKS and ECS on the AWS website.

    For this post we will use ECS.

    The chosen one and the sidekick

    With ECS chosen, now you have to find a preferably easy way to deploy your containerized application on it.

    There are quite a number of resources from AWS that are needed for your application to live on ECS, such as VPC (Virtual Private Cloud), Security Group (firewall), EC2 (virtual machine), Load Balancer, and others. Creating these resources manually is cumbersome so AWS has came out with a tool that can automate the creation of all of them. The tool is known as AWS Copilot and we are going to learn how to use it.

    Install Docker

    Docker or Docker Desktop is required for building the Docker image later. Please refer to my previous article on how to install Docker Desktop on macOS, or follow Docker’s instructions for Linux and Windows.

    Set up AWS CLI

    We need to set up the Docker AWS CLI (command-line interface) for authentication and authorization to AWS.

    Execute the following command to install the AWS CLI on macOS:

    $ curl -O "https://awscli.amazonaws.com/AWSCLIV2.pkg"
    $ sudo installer -pkg AWSCLIV2.pkg -target /
    

    For other OSes see Amazon’s docs.

    Execute the following command and enter the AWS Account and Access Keys.

    $ aws configure
    

    Install AWS Copilot CLI

    Now it’s time for the main character: AWS Copilot.

    Install AWS Copilot with Homebrew for macOS:

    $ brew install aws/tap/copilot-cli
    

    See AWS Copilot Installation for other platforms.

    The Django project

    Create a Django project by using a Python Docker Image. You can clone my Git project to get the Dockerfile, docker-compose.yaml and requirements.txt that I’m using.

    $ git clone https://github.com/aburayyanjeffry/django-copilot.git
    

    Go to the django-pilot directory and execute docker-compose to create a Django project named “mydjango”.

    $ cd django-copilot
    $ docker-compose run web django-admin startproject mydjango .
    

    Edit mydjango/settings.py to allow all hostnames for its URL. This is required because by default AWS will generate a random URL for the application. Find the following variable and set the value as follows:

    ALLOWED_HOSTS = ['*']
    

    The Deployment with AWS Copilot

    Create an AWS Copilot “Application”. This is a grouping of services such as web app or database, environments (development, QA, production), and CI/CD pipelines. Execute the following command to create an Application with the name of “mydjango”.

    $ copilot init -a mydjango
    

    Select the Workload type. Since this Django is an internet-facing app we will choose the “Load Balanced Web Service”.

    Which workload type best represents your architecture?  [Use arrows to move, type to filter, ? for more help]
        Request-Driven Web Service  (App Runner)
      > Load Balanced Web Service   (Internet to ECS on Fargate)
        Backend Service             (ECS on Fargate)
        Worker Service              (Events to SQS to ECS on Fargate)
        Scheduled Job               (Scheduled event to State Machine to Fargate)
    

    Give the Workload a name. We are going to name it “mydjango-web”.

    Workload type: Load Balanced Web Service
    
      What do you want to name this service? [? for help] mydjango-web
    

    Select the Dockerfile in the current directory.

    Which Dockerfile would you like to use for mydjango-web?  [Use arrows to move, type to filter, ? for more help]
      > ./Dockerfile
        Enter custom path for your Dockerfile
        Use an existing image instead
    

    Accept to create a test environment.

    All right, you're all set for local development.
    
      Would you like to deploy a test environment? [? for help] (y/N) y
    

    Wait and see. At the end of the deployment you will get the URL of your application. Open it in a browser.

    Sample output of AWS copilot init run

    Sample view from a browser of a Django app default debug page stating “The install worked successfully! Congratulations!

    Now let’s migrate some data, create a superuser, and try to log in. The Django app comes with a SQLite database. Execute the following command to get a terminal for the Django app:

    $ copilot svc exec
    

    Once in the terminal, execute the following to migrate the initial data and to create the superuser:

    $ python manage.py migrate
    $ python manage.py createsuperuser
    

    Output from Django database migration run

    Now you may access the admin page and login by using the created credentials.

    Django login page screenshot

    You should see the Django admin:

    Django admin page screenshot after successful login

    A mini cheat sheet

    AWS Copilot commands Remarks
    copilot app ls To list available Applications
    copilot app show -n appname To get the details of an Application
    copilot app delete -n appname To delete an Application
    copilot svc ls To list available Services
    copilot svc show -n svcname To get the details of a Service
    copilot svc delete -n svcname To delete a Service

    The End

    That’s all, folks.

    AWS Copilot is a tool to automate the deployment of AWS infrastructure for our containerized application needs. It takes away most of the worries about infrastructure and enables us to focus sooner on the application development.

    For further info on AWS Copilot visit its website.


    docker containers cloud aws python django

    Getting started with Docker and Kubernetes on macOS

    Jeffry Johar

    By Jeffry Johar
    June 20, 2022

    Shipping infrastructure at a dock

    What is the best way to master American English? One school of thought says that the best way to learn a language is to live in the country of the origin. For American English that would be the USA. Why is that so? Because we as the learners get to talk to native speakers daily. By doing this, we get to know how the natives use the language and its grammar in the real world.

    The same goes for learning Docker and Kubernetes. The best way to learn Docker and Kubernetes is to get them in our MacBooks, laptops, and PCs. This way we can learn and try locally what works and what doesn’t work in our local host at any time, any day.

    Lucky for us earthlings who enjoy GUIs, Docker now has Docker Desktop. As its name suggests, it is nicely built for the desktop. It comes with GUI and CLI to manage our Docker and Kubernetes needs. Please take note of the Docker Desktop license. It is free for personal use, education, and open source projects, and has a fee for enterprise usage. With that out of the way, let’s get things started.

    Docker Desktop Installation

    The official Docker Desktop for can be found on Docker’s website. It covers installation for macOS, Linux, and Windows. For this post we are going to install Docker Desktop for macOS using Brew. Execute the following command to proceed with the Docker Desktop installation:

    brew install --cask docker
    

    Installing Docker Desktop with Brew

    Then run it at Finder ➝ Application ➝ Docker.

    Docker in the Finder list of Applications

    Upon a successful installation, Docker Desktop will appear as the following:

    Screenshot of Docker Desktop

    Click the Docker icon at the top menu bar to ensure the Docker Desktop is running.

    The Docker icon in the top menu bar of macOS

    Run the first containerized application

    For the first application we are going to run the latest version of nginx official image from hub.docker.com. Open the terminal and execute the following to run the nginx image as a background service at port 80:

    docker run -d -p 80:80 nginx:latest
    

    Run the following command to check on the application. This is the Docker equivalent to the standard ps Unix command to list processes.

    docker ps
    

    curl the application at localhost port 80:

    curl http://localhost
    

    Sample output:

    curl outputting the default nginx HTML

    Stop the application

    Execute the following command to get the application information and take note of the container ID.

    docker ps
    

    Execute the following to stop the application by its container ID. In the following example the container ID is f7c19b95fcc2.

    docker stop {container ID}
    

    Run docker ps again to ensure that the stopped application is not being display as a running application.

    Sample Output:

    Docker ps output container list

    Enable Kubernetes

    Click the Docker icon at the top menu bar and click Preferences:

    The preferences under Docker’s menu icon

    Enable Kubernetes and click Apply and Restart:

    Kubernetes enable button in Docker Desktop preferences

    Click the Docker icon at the top menu bar and ensure the Kubernetes is running:

    Docker icon menu, now showing that Kubernetes is running

    Open the terminal and check on the Kubernetes nodes. The status should be Ready:

    kubectl get nodes
    

    Docker Desktop node running

    Deploy the first application on Kubernetes

    We are going to deploy the same official latest nginx images at Kubernetes. Execute the following command:

    kubectl run mynginx --image=nginx:latest
    

    Execute the following command to check on the application. Its status should be Running. On a slow machine this will take some time and we might need to do this multiple times.

    kubectl get pod
    

    Execute the following command to create the Kubernetes service resource for the application. A service in Kubernetes serves as an internal named load balancer.

    kubectl expose pod mynginx --port 80
    

    Execute the following command to redirect the localhost network to the Kubernetes network. By doing this we can curl or access the application from localhost:{port}. This is a foreground process, so it needs to be left open.

    kubectl port-forward service/mynginx 8080:80
    

    Open another terminal to curl localhost:8080 or open the address in a web browser.

    curl http://localhost:8080
    

    Curl showing nginx’s default html output

    In a browser:

    Nginx’s default output in a browser

    Clean up the Kubernetes resources

    Ctrl-C at the port-forwarding process in the terminal and list all the running Kubernetes resources. We should see our application in a pod and its services:

    kubectl get all
    

    kubectl with mynginx pod’s age and service highlighted as 21 minutes

    Now we need to delete these resources.

    To delete the service:

    kubectl delete service/mynginx
    

    To delete the application:

    kubectl delete pod/mynginx
    

    Now list back all resources. The mynginx-related resources should not be displayed.

    kubectl get all
    

    kubectl listing the kubernetes service

    To stop Docker Desktop

    If we are done with Docker Desktop we can stop its services by going to the top menu bar and selecting the Quit Docker Desktop option. This will stop Docker and Kubernetes services.

    A Quite Docker Desktop button under the Docker icon menu in the top menu bar

    That’s all, folks. We now have the tools to learn and explore Docker and Kubernetes in our own local host.

    Now we may proceed with the official documentation and other tutorials to continue on the path to learn Docker and Kubernetes.


    docker kubernetes containers

    Implementing Authentication in ASP.NET Core Web APIs

    Kevin Campusano

    By Kevin Campusano
    June 17, 2022

    Several buildings with a lightly cloudy blue sky

    Authentication is a complex space. There are many problem scenarios and many more solutions. When it comes to Web APIs written with ASP.NET Core, there are various fully featured options like Duende IdentityServer or Azure Active Directory. These promise to be “everything but the kitchen sink” solutions which are robust and allow you to deal with many complex requirements.

    But what if our requirements dictate that we need something simpler? Do we have to roll out our own from scratch? Or does ASP.NET Core offer smaller, customizable, somewhat independent puzzle pieces that we can put together without having to write all the code ourselves and still have a good amount of control?

    Spoiler alert: The answer to that last question is yes. And we’re going to talk about it in this very article.

    There is a Table of contents at the end of this post.

    Two approaches to authentication: JWT and API Keys

    In this article, we’ll take an existing ASP.NET Core Web API and add authentication capabilities to it. Specifically, we’ll support two authentication schemes commonly used for Web APIs: JWT and API Keys. Also, we will use our own database for storage of user accounts and credentials.

    The project that we will work with is a simple ASP.NET Web API backed by a Postgres database. It has a few endpoints for CRUDing automotive related data and for calculating values of vehicles based on various aspects of them. You can read all about the process of building it here. I also added a few database integration tests for it here.

    You can find it on GitHub. If you’d like to follow along, clone the repository and checkout this commit: cd290c765fcd2c6693008d3dc76fa931098dcaa0. It represents the project as it was before applying all the changes from this article.

    You can follow the instructions in the project’s README file in order to get the app up and running.

    Managing user accounts with ASP.NET Core Identity

    Let’s deal first with the requirement of storing the user accounts in our own database.

    Luckily for us, ASP.NET Core provides us with Identity. This is an API that offers a comprehensive solution for authentication. It can connect with the aforementioned Duende IdentityServer for OpenID Connect and OAuth 2.0, supports authentication via third parties like Facebook or Google, and can fully integrate with user interfaces on web apps, complete with scaffolding/​autogeneration capabilities too.

    Most importantly for us, it supports management of user accounts stored in our own database. It includes data for third party logins, passwords, roles for authorization, email confirmation, access tokens, etc.

    That’s a lot of of functionality baked in there. But we don’t need all that, so let’s see how we can take only the bits and pieces that we need in order to fulfill our specific requirements.

    Specifically, we want:

    1. Tables in our database for storage of user accounts and passwords.
    2. A programmatic way to create, fetch and validate users using those tables.

    It’s actually pretty simple.

    Install the necessary NuGet packages

    First, we need to install a couple of NuGet packages:

    We can install them by running these two commands from the VehicleQuotes directory:

    $ dotnet add package Microsoft.AspNetCore.Identity
    $ dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
    

    That will add the following lines to the VehicleQuotes/VehicleQuotes.csproj project file:

     <Project Sdk="Microsoft.NET.Sdk.Web">
       ...
       <ItemGroup>
         ...
    +    <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
    +    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.5" />
         ...
       </ItemGroup>
     </Project>
    

    Update the DbContext to include the Identity tables

    Next step is to configure our DbContext class so that it includes the new tables that we need from the Identity library. So let’s go to VehicleQuotes/Data/VehicleQuotesContext.cs and update it to do so.

    We need to include these new using statements at the top of the file so that we have access to the classes that we need from the NuGet packages we just installed:

    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    

    Next, instead of DbContext, the VehicleQuotesContext class should inherit from IdentityUserContext<IdentityUser>.

    IdentityUserContext is a class provided by ASP.NET Core Identity that’s designed so that our DbContext can inherit from it and gain user management functionality. Namely, it includes a new DbSet (and consequently, a table) for holding user accounts (aptly named Users), among other things that we won’t need.

    IdentityUserContext has a more feature rich counterpart called IdentityDbContext, which also includes DbSets to support roles based authorization. We don’t need all that so we use its simpler cousin. Feel free to explore the source code on GitHub to see all it offers.

    The generic type parameter that we give it, IdentityUser, is a class that’s also provided by the Identity library. Its purpose is to serve as a default Entity Type for our user model and, as a consequence, our Users DbSet.

    In summary, by having our DbContext class inherit from IdentityUserContext<IdentityUser>, we’re telling Identity that we want it to augment our DbContext (and database) to include the core user management tables and that we want our users table to have the same columns as IdentityUser.

    If we wanted to include more columns in our users table, what we would have to do is create a new class, make it inherit from IdentityUser, define any additional fields that we want on it, and use that class as a type parameter to IdentityUserContext. For us for now, the default works just fine. You can learn more about customizing Identity in the official docs.

    The change looks like this:

    -public class VehicleQuotesContext : DbContext
    +public class VehicleQuotesContext : IdentityUserContext<IdentityUser>
    

    Finally, IdentityUserContext has some logic that it needs to run when it is being created. In order to allow it to run that logic, let’s add the following line to our VehicleQuotesContext’s OnModelCreating method:

     protected override void OnModelCreating(ModelBuilder modelBuilder)
     {
    +    base.OnModelCreating(modelBuilder);
         // ...
     }
    

    This calls IdentityUserContext’s own OnModelCreating implementation so that it can set itself up properly.

    The complete file should be looking like this now:

    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    using VehicleQuotes.Models;
    
    namespace VehicleQuotes
    {
        public class VehicleQuotesContext : IdentityUserContext<IdentityUser>
        {
            public VehicleQuotesContext (DbContextOptions<VehicleQuotesContext> options)
                : base(options)
            {
            }
    
            public DbSet<Make> Makes { get; set; }
            public DbSet<Size> Sizes { get; set; }
            public DbSet<BodyType> BodyTypes { get; set; }
    
            public DbSet<Model> Models { get; set; }
            public DbSet<ModelStyle> ModelStyles { get; set; }
            public DbSet<ModelStyleYear> ModelStyleYears { get; set; }
    
            public DbSet<QuoteRule> QuoteRules { get; set; }
            public DbSet<QuoteOverride> QuoteOverides { get; set; }
    
            public DbSet<Quote> Quotes { get; set; }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
    
                modelBuilder.Entity<Size>().HasData(
                    new Size { ID = 1, Name = "Subcompact" },
                    new Size { ID = 2, Name = "Compact" },
                    new Size { ID = 3, Name = "Mid Size" },
                    new Size { ID = 5, Name = "Full Size" }
                );
    
                modelBuilder.Entity<BodyType>().HasData(
                    new BodyType { ID = 1, Name = "Coupe" },
                    new BodyType { ID = 2, Name = "Sedan" },
                    new BodyType { ID = 3, Name = "Hatchback" },
                    new BodyType { ID = 4, Name = "Wagon" },
                    new BodyType { ID = 5, Name = "Convertible" },
                    new BodyType { ID = 6, Name = "SUV" },
                    new BodyType { ID = 7, Name = "Truck" },
                    new BodyType { ID = 8, Name = "Mini Van" },
                    new BodyType { ID = 9, Name = "Roadster" }
                );
            }
        }
    }
    

    Applying database changes

    The Users DbSet that we added into our data model by inheriting from IdentityUserContext<IdentityUser> will become a table once we create and apply a database migration. That’s what we will do next.

    That’s simple in ASP.NET Core. All we need to do is run a command like this:

    dotnet ef migrations add AddIdentityTables
    

    That should produce a new migration file named something like 20220605003253_AddIdentityTables.cs. If you explore it, you’ll see how it contains the definitions for a few new database tables. Including the one that we want: AspNetUsers. That’s the one that we will use to store our user account records.

    Next, we apply the migration with:

    dotnet ef database update
    

    If you now connect to the database, you’ll see the new tables in there.

    If you have the database running in a Docker container like we discussed in the beginning of the article, you should be able to connect to it with:

    $ psql -h localhost -U vehicle_quotes
    

    Then, to see the tables:

    vehicle_quotes=# \dt
                        List of relations
     Schema |         Name          | Type  |     Owner      
    --------+-----------------------+-------+----------------
     public | AspNetUserClaims      | table | vehicle_quotes
     public | AspNetUserLogins      | table | vehicle_quotes
     public | AspNetUserTokens      | table | vehicle_quotes
     public | AspNetUsers           | table | vehicle_quotes
     ...
    (14 rows)
    

    And that’s pretty much it when it comes to having a sensible storage for user accounts. That was pretty inexpensive, wasn’t it? All we had to do was install some NuGet packages, tweak our existing DbContext, and run some migrations.

    The best thing is that we’re not done yet. We can also take advantage of ASP.NET Core Identity to manage users programmatically. Instead of interacting with these tables directly, we will use the service classes provided by Identity to create new users, fetch existing ones and validate their credentials.

    Before we can do that though, we must first do some configuration in our app’s Startup class so that said services are properly set up to our liking and are made available to our application.

    Configuring the Identity services

    We need to add some code to VehicleQuotes/Startup.cs. With it, we configure the Identity system and add a few service classes to ASP.NET Core’s IoC container so that they are available to our app via Dependency Injection.

    We need a new using statement:

    using Microsoft.AspNetCore.Identity;
    

    And the following code added to the ConfigureServices method:

    services
        .AddIdentityCore<IdentityUser>(options => {
            options.SignIn.RequireConfirmedAccount = false;
            options.User.RequireUniqueEmail = true;
            options.Password.RequireDigit = false;
            options.Password.RequiredLength = 6;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = false;
            options.Password.RequireLowercase = false;
        })
        .AddEntityFrameworkStores<VehicleQuotesContext>();
    

    The call to AddIdentityCore makes several Identity utility classes available to the application. Among those, UserManager is the only one we will use. We will use it later to… well, manage users. You can also see how we’ve set a few options related to how the user accounts are handled. options.SignIn.RequireConfirmedAccount controls whether new accounts need to be confirmed via email before they are available. With options.User.RequireUniqueEmail, we tell Identity to enforce uniqueness of emails on user accounts. And finally the options.Password.* options configure the password strength requirements.

    You can explore all the available options in the official docs.

    Then, the call to AddEntityFrameworkStores tells the Identity system that it should use our VehicleQuotesContext for data storage.

    Creating users

    With that configuration out of the way, we can now write some code to create new user accounts. To keep things simple, we’ll add a new UsersController to our project that will expose a new endpoint that offers that functionality.

    Let’s start with this in VehicleQuotes/Controllers/UsersController.cs:

    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    
    namespace VehicleQuotes.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class UsersController : ControllerBase
        {
            private readonly UserManager<IdentityUser> _userManager;
    
            public UsersController(
                UserManager<IdentityUser> userManager
            ) {
                _userManager = userManager;
            }
        }
    }
    

    As you can see, this controller defines a dependency on UserManager<IdentityUser>, an instance of which is injected via the constructor. This is one of the classes made available to us when we configured the Identity core services in our app’s Startup. We will use it to create new user records.

    Before that though, we need to define a new class that encapsulates the payload for a request to our endpoint. When it comes to creating user accounts, all we need is a username, a password, and an email. As such, we add the following class in a new VehicleQuotes/ResourceModels/User.cs file:

    using System.ComponentModel.DataAnnotations;
    
    namespace VehicleQuotes.ResourceModels
    {
        public class User
        {
            [Required]
            public string UserName { get; set; }
            [Required]
            public string Password { get; set; }
            [Required]
            public string Email { get; set; }
        }
    }
    

    Now, we shall use this type as the parameter for a new PostUser action method in the UserController which will expose the new user account creation endpoint in our API. The method looks like this:

    // POST: api/Users
    [HttpPost]
    public async Task<ActionResult<User>> PostUser(User user)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        var result = await _userManager.CreateAsync(
            new IdentityUser() { UserName = user.UserName, Email = user.Email },
            user.Password
        );
    
        if (!result.Succeeded)
        {
            return BadRequest(result.Errors);
        }
    
        user.Password = null;
        return Created("", user);
    }
    

    Be sure to also add the following using statements:

    using System.Threading.Tasks;
    using VehicleQuotes.ResourceModels;
    

    System.Threading.Tasks allows us to reference the class Task<> which is the return type of the new PostUser method. VehicleQuotes.ResourceModels is where our new User class lives.

    Thanks to the instance of UserManager<IdentityUser> that we’re holding onto, this method is very straightforward. The most interesting portion is this:

    var result = await _userManager.CreateAsync(
        new IdentityUser() { UserName = user.UserName, Email = user.Email },
        user.Password
    );
    

    Here we’re newing up an IdentityUser instance using the given request parameters and passing it on to UserManager<IdentityUser>’s CreateAsync method, along with the password that was also given in the incoming request. This puts the Identity system to work for us and properly create a new user account.

    Then, we can inspect its return value (which we capture in the result variable) to determine if the operation was successful. That way we can respond appropriately to our API’s caller.

    Finally, with…

    user.Password = null;
    return Created("", user);
    

    we return the data representing the newly created user account but we’re discreet and make sure not to include the password.

    With that, we can test our API. Fire it up with dotnet run (or dotnet watch!) and send a POST request to our new endpoint at http://0.0.0.0:5000/api/Users with a payload like this:

    {
      "userName": "newuser000",
      "email": "newuser000@endpointdev.com",
      "password": "password"
    }
    

    I just tried it in Postman and this is what it looked like:

    Successful user creation request in Postman

    Feel free to test it out further. Try repeated emails or usernames. Try passwords that don’t meet the criteria we defined when configuring Identity in the app’s Startup class. It all works as you’d expect.

    You can inspect the database and see the newly created record too:

    vehicle_quotes=# select id, user_name, email, password_hash from "AspNetUsers";
    -[ RECORD 1 ]-+--------------------------------------
    id            | aaad07b4-f109-4255-8caa-185fd7694c72
    user_name     | newuser000
    email         | newuser000@endpointdev.com
    password_hash | AQAAAAEAACcQAAAAEJJTV7M2Ejqd3K3iC...
    

    And there it is, that’s our record. With a hashed password and everything.

    Now let’s see what would an endpoint to fetch users look like:

    Fetching users

    UserManager<IdentityUser> offers a FindByNameAsync method that we can use to retrieve users by name. We can add a new endpoint that leverages it like so:

    // GET: api/Users/username
    [HttpGet("{username}")]
    public async Task<ActionResult<User>> GetUser(string username)
    {
        IdentityUser user = await _userManager.FindByNameAsync(username);
    
        if (user == null)
        {
            return NotFound();
        }
    
        return new User
        {
            UserName = user.UserName,
            Email = user.Email
        };
    }
    

    This is even more straightforward than the previous one. We just call the FindByNameAsync method by giving it the username of the account we want, make sure that we actually found it, and then return the data.

    For the response, we use the same User class that we created to represent the input for the creation endpoint. If we added more fields to the user profile, we could include them here. Alas, we only have username and email for now.

    Restart the app and we can now make a request like this:

    Successful user fetch request in Postman

    Pretty neat, huh? Try a username that does not exist and you should see the API respond with a 404.

    One quick improvement that we can do before we move on: let’s change PostUser’s return statement to this:

    return CreatedAtAction("GetUser", new { username = user.UserName }, user);
    

    All that does is include a Location header on the POST Users endpoint’s response that contains the URL for the newly created user. That’s just being a good citizen.

    Implementing JWT Bearer Token authentication

    Now that we have the user management capabilities that we need, let’s implement some actual authentication. We will start by adding JWT-based authentication to our API.

    The strategy that we will use is to create a new API endpoint that clients can POST credentials to and that will respond to them with a fresh, short-lived token. They will then be able to use that token for subsequent requests by including it via headers.

    We will pick a random endpoint to secure, just to serve as an example. That is, an endpoint that requires authentication in order to be accessed. GET api/BodyTypes is a good candidate. It is defined in VehicleQuotes/Controllers/BodyTypesController.cs’s GetBodyTypes action method. Feel free to test it out.

    Creating tokens

    Let’s start by creating the new endpoint that clients will call to obtain the auth tokens. In order to do so, we need a few things:

    1. A class that represents incoming request data.
    2. A class that represents outgoing response data.
    3. A class that can generate tokens for a given user.
    4. A new action method in UsersController that does the work.
    The request data structure

    To deal with step one, let’s define a new class in VehicleQuotes/ResourceModels/AuthenticationRequest.cs that looks like this:

    using System.ComponentModel.DataAnnotations;
    
    namespace VehicleQuotes.ResourceModels
    {
        public class AuthenticationRequest
        {
            [Required]
            public string UserName { get; set; }
            [Required]
            public string Password { get; set; }
        }
    }
    

    All users need to authenticate is provide a set of credentials: username and password. So we have this class that contains fields for both and that will be used as input for our endpoint.

    The response data structure

    Next, we need to define a class to represent that endpoint’s response. I’ve added the following class in VehicleQuotes/ResourceModels/AuthenticationResponse.cs:

    using System;
    
    namespace VehicleQuotes.ResourceModels
    {
        public class AuthenticationResponse
        {
            public string Token { get; set; }
            public DateTime Expiration { get; set; }
        }
    }
    

    Just a simple data structure containing the token itself and a date letting clients know when they can expect it to expire.

    The class that creates JWTs

    Step 3 is creating a class that can produce the tokens. This is the most interesting part in terms of complexity. Before we can do that though, let’s add the following configurations to VehicleQuotes/appsettings.json:

      "Jwt": {
        "Key": "this is the secret key for the jwt, it must be kept secure",
        "Issuer": "vehiclequotes.endpointdev.com",
        "Audience": "vehiclequotes.endpointdev.com",
        "Subject": "JWT for vehiclequotes.endpointdev.com"
      },
    

    These are values that we’ll need when creating the tokens. We’ll go over the purpose of each them as they come up as we continue writing our code.

    Here we’ll gloss over some details on the inner workings of JWTs as a standard. You can learn more about them at jwt.io.

    For now, let’s add the following class in VehicleQuotes/Services/JwtService.cs:

    using System;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Configuration;
    using Microsoft.IdentityModel.Tokens;
    using VehicleQuotes.ResourceModels;
    
    namespace VehicleQuotes.Services
    {
        public class JwtService
        {
            private const int EXPIRATION_MINUTES = 1;
    
            private readonly IConfiguration _configuration;
    
            public JwtService(IConfiguration configuration)
            {
                _configuration = configuration;
            }
    
            public AuthenticationResponse CreateToken(IdentityUser user)
            {
                var expiration = DateTime.UtcNow.AddMinutes(EXPIRATION_MINUTES);
    
                var token = CreateJwtToken(
                    CreateClaims(user),
                    CreateSigningCredentials(),
                    expiration
                );
    
                var tokenHandler = new JwtSecurityTokenHandler();
    
                return new AuthenticationResponse {
                    Token = tokenHandler.WriteToken(token),
                    Expiration = expiration
                };
            }
    
            private JwtSecurityToken CreateJwtToken(Claim[] claims, SigningCredentials credentials, DateTime expiration) =>
                new JwtSecurityToken(
                    _configuration["Jwt:Issuer"],
                    _configuration["Jwt:Audience"],
                    claims,
                    expires: expiration,
                    signingCredentials: credentials
                );
    
            private Claim[] CreateClaims(IdentityUser user) =>
                new[] {
                    new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.Email, user.Email)
                };
    
            private SigningCredentials CreateSigningCredentials() =>
                new SigningCredentials(
                    new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])
                    ),
                    SecurityAlgorithms.HmacSha256
                );
        }
    }
    

    The CreateToken method is the element that’s most worth discussing here. It receives an instance of IdentityUser as a parameter (which, remember, is the entity class that represents our user accounts), uses it to construct a JWT, and returns it within a AuthenticationResponse object (which is what we decided that our endpoint would return). To do so, we use various classes built into .NET.

    The main class that represents the JWT is JwtSecurityToken. We new up one of those in the CreateJwtToken method with this call:

    new JwtSecurityToken (
        _configuration["Jwt:Issuer"],
        _configuration["Jwt:Audience"],
        claims,
        expires: expiration,
        signingCredentials: credentials
    );
    

    “Issuer” and “Audience” are two important values for how JWTs work. They specify which entity is creating the token (i.e. the “Issuer”) and which entity is the token intended for (i.e. the “Audience”). We use the IConfiguration instance that we got as a dependency to fetch their values from the VehicleQuotes/appsettings.json file.

    The “Issuer” and “Audience” parameters in JwtSecurityToken’s constructor correspond to JWT claims iss and aud, respectively. You can learn more about them and other claims in the RFC.

    The next parameter that JwtSecurityToken’s constructor needs is the claims array. In JWT terms, a claim is essentially a statement about the entity for which the token is generated, some data that identifies it. For example, if we’re generating a token for a user, what you would expect to see in such a token’s claims are things like username, email, and any other non-secret profile info.

    In our case, as you can see in the CreateClaims method, we add a number of claims:

    new[] {
        new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
        new Claim(ClaimTypes.NameIdentifier, user.Id),
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.Email, user.Email)
    };
    

    Along with the user id, name and email, we also add sub, jti and iat claims. These are standardized claims of which you can learn more about in the RFC. The data that we put in here will make its way into the encoded token that our API caller eventually sees. We’ll see that later.

    The next parameter to JwtSecurityToken is the expiration date of the token. Here we are setting it to just one minute in the future to comply with our original requirement that the token should be short-lived so that it only allows a handful of requests over a short period of time.

    Finally there’s the signingCredentials parameter which tells the JwtSecurityToken how to cryptographically sign the token. As you can see in the code:

    new SigningCredentials(
        new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])
        ),
        SecurityAlgorithms.HmacSha256
    );
    

    That’s a SigningCredentials instance that we create using the “key” that we have configured in VehicleQuotes/appsettings.json, along with the algorithm to use to produce it.

    And that’s about it. There isn’t much else to this class. It is somewhat involved but that’s just how JWTs are created in .NET.

    The action method that puts it all together

    Now all we need to do is actually create an endpoint that exposes this functionality to clients. Since we have the core logic encapsulated in our JwtService class, the actual action method is simple. Here are the changes that we need to make in order to implement it:

    First, on VehicleQuotes/Controllers/UsersController.cs, we add a new using statement so that we can reference our new JwtService class:

    using VehicleQuotes.Services;
    

    Next, we declare a new parameter of type JwtService in the controller’s constructor so that we signal to ASP.NET Core’s Dependency Injection subsystem that we want to use one of those. We also hold onto it via a new instance variable called _jwtService:

     // ...
     public class UsersController : ControllerBase
     {
         private readonly UserManager<IdentityUser> _userManager;
    +    private readonly JwtService _jwtService;
     
         public UsersController(
             UserManager<IdentityUser> userManager,
    +        JwtService jwtService
         ) {
             _userManager = userManager;
    +        _jwtService = jwtService;
         }
         // ...
     }
    

    We also need to tell ASP.NET Core that JwtService should be available for Dependency Injection. To do so, we can add this line in VehicleQuotes/Startup.cs’s ConfigureServices method:

    services.AddScoped<Services.JwtService>();
    

    Note that this way of defining dependencies is not recommended and only done this way here to keep things simple for an illustrative app that’s never going to run in production. Ideally what you want to do here is define the dependency as an abstraction (i.e. an interface) and a concrete implementation that fulfills it. For example:

    services.AddScoped<ITokenCreationService, JwtService>();
    

    This way, classes that depend on this service can reference the interface, not the concrete type. In doing that, they adhere to the Dependency Inversion principle and become more easily testable because they allow mocks to be provided as dependencies.

    Finally, we write the actual action method:

    // POST: api/Users/BearerToken
    [HttpPost("BearerToken")]
    public async Task<ActionResult<AuthenticationResponse>> CreateBearerToken(AuthenticationRequest request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest("Bad credentials");
        }
    
        var user = await _userManager.FindByNameAsync(request.UserName);
    
        if (user == null)
        {
            return BadRequest("Bad credentials");
        }
    
        var isPasswordValid = await _userManager.CheckPasswordAsync(user, request.Password);
    
        if (!isPasswordValid)
        {
            return BadRequest("Bad credentials");
        }
    
        var token = _jwtService.CreateToken(user);
    
        return Ok(token);
    }
    

    Here too we’re leveraging the UserManager instance that ASP.NET Core Identity so graciously provided us with. In summary, we find the user account by name using the incoming request data, check if the given password is correct, then ask our JwtService to create a token for this user, and finally return it wrapped in a 200 response.

    If you hit that endpoint with a POST and a set of existing credentials, you should see something like this:

    Successful JTW creation in Postman

    If you take that big string that came back in the "token" field in the response JSON, and paste it in jwt.io’s token decoder, you should be able to see all the claims that we added to the token:

    The JTW decoded showing all the claims

    Notice that the keywords here are “Encoded” and “Decoded”. The claims that we put in our JWTs are not protected in any way. As such, we should never put secrets in there.

    Securing an endpoint with JWT authentication

    Now that we have a way for obtaining tokens, let’s see how we can actually use them to gain access to some resources. To demonstrate that, let’s secure an endpoint in a way that it denies unauthenticated requests.

    Applying the Authorize attribute

    First we need to signal ASP.NET Core that the endpoint requires auth. We do that by annotating the corresponding action method with the Authorize attribute. We’ve already decided that we were going to use VehicleQuotes/Controllers/BodyTypesController.cs’s GetBodyTypes method as a guinea pig. So, let’s go into that file and add the following using statement:

    using Microsoft.AspNetCore.Authorization;
    

    That will allow us access to the attribute, which we can apply to the action method like so:

     // GET: api/BodyTypes
    + [Authorize]
      [HttpGet]
      public async Task<ActionResult<IEnumerable<BodyType>>> GetBodyTypes()
      {
          return await _context.BodyTypes.ToListAsync();
      }
    

    With this, we’ve told ASP.NET Core that we want it to require auth for this endpoint.

    Enabling and configuring the JWT authentication

    Now, we need to tell it how to actually perform the check. To do that, we need to install the Microsoft.AspNetCore.Authentication.JwtBearer NuGet package. We can do that from the VehicleQuotes directory with the following command:

    $ dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
    

    Once we have that installed, we get access to additional services which we can use to configure the “JwtBearer” authentication scheme. As usual, we do the configuration in VehicleQuotes/Startup.cs’s. Here’s what we need to do:

    Add a few new using statements:

    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.IdentityModel.Tokens;
    using System.Text;
    

    Add the following code at the end of the ConfigureServices method:

    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidAudience = Configuration["Jwt:Audience"],
                ValidIssuer = Configuration["Jwt:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])
                )
            };
        });
    

    The call to AddAuthentication includes all the internal core service classes that are needed to do authentication in our app.

    The JwtBearerDefaults.AuthenticationScheme parameter that we give it is the name of the authentication scheme to use as the default. More on that later. For now, know that ASP.NET Core supports multiple authentication schemes to be used at the same time. In fact, our own plan here is to eventually support two auth schemes: JWTs and API Keys. We’re starting with JWT first and as such, that’s the one we specify as the default.

    The call to AddJwtBearer configures the JWT authentication scheme. That is, it allows the app to perform authentication checks based on an incoming JWT token.

    The most important part of the configuration is the values that we are passing to TokenValidationParameters. As you can see, we are able to specify which aspects of the incoming JWTs to validate. You can see all available options for TokenValidationParameters in the official documentation. Here we’ve chosen to validate obvious things like the issuer, audience, and signing key.

    The last configuration step is to add this line right before app.UseAuthorization(); in the Configure method:

    app.UseAuthentication();
    

    That will enable the authentication middleware. That way the framework will perform authentication checks as part of the request processing pipeline. In other words, actually put all the configuration that we’ve done to good use.

    Alright, now that we have all the configuration ready and have secured an endpoint, let’s try hitting it with a GET on api/BodyTypes and see what happens:

    Unauthorized response for unauthenticated request

    Ok! So far so good. We made an unauthenticated request into an endpoint that requires authentication and as a result we got a 401 back. That’s just what we wanted.

    Now, let’s get a token by POSTing to api/Users/BearerToken:

    Another successful JTW creation in Postman

    We can copy that token and include as a header in the GET request to api/BodyTypes. The header key should be Authorization and the value should be Bearer <our token>. In Postman, we can setup a similar request if we choose the “Authorization” tab, select “Bearer Token” in the “Type” drop-down list and paste the token in the “Token” text box.

    Do that, and you should now see a 200 response from the endpoint:

    Successful response for request authenticated with JWT

    Neat! Now that we passed a valid token, it wants to talk to us again. Let a few minutes pass, enough for the token to expire and try the request again to see how it’s 401’ing again.

    Implementing API Key authentication

    Now let’s change gears from JWT and implement an alternate authentication strategy in our Web API: API Keys. Authentication with API Keys is fairly common in the web service world.

    The core idea of API Keys is that the API provider (in this case, us) produces a secret string that is given to the clients for safekeeping. The clients then can use that key to access the API and make as many requests as they want. So it’s essentially just a glorified password.

    The main functional differences when it comes to JWT as we have implemented it is that the API Keys won’t expire and that we will store them in our database. Then, when processing incoming requests, as part of the authentication step, our app will check if the given API Key exists in our internal records. If it does, then the request is allowed to go through, if it does not, then a 401 is sent back.

    This is going to be interesting because ASP.NET Core does not include an authentication scheme out of the box for this kind of behavior. For JWT, we were able to use the Microsoft.AspNetCore.Authentication.JwtBearer package. For this we have to get our hands dirty and actually implement a custom authentication handler that will run the logic that we need.

    Let’s get started.

    Creating API Keys

    Similarly to when we implemented JWTs, we’ll start by creating the new endpoint that clients will call to obtain their keys. In order to make it work, we’ll need:

    1. A new model and database table to store the keys
    2. A service class to create the keys.
    3. A new action method in UsersController that does the work.
    The API Key model and database table

    For storing API Keys, we just need a simple table that is linked to users via a one-to-many relationship and includes a name and a value. Using Entity Framework, our model could look like this in VehicleQuotes/Models/UserApiKey.cs:

    using System.ComponentModel.DataAnnotations;
    using System.Text.Json.Serialization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    
    namespace VehicleQuotes.Models
    {
        [Index(nameof(Value), IsUnique = true)]
        public class UserApiKey
        {
            [JsonIgnore]
            public int ID { get; set; }
    
            [Required]
            public string Value { get; set; }
    
            [JsonIgnore]
            [Required]
            public string UserID { get; set; }
    
            [JsonIgnore]
            public IdentityUser User { get; set; }
        }
    }
    

    Easy enough. We express the relationship between the “keys” and “users” tables via the User navigation property and the UserID field. We also define a unique index on the Value field and annotate some fields with Required because we don’t want them to allow null. Interestingly, we also annotated some of the fields with the JsonIgnore attribute so that when we include objects of this type in API responses (which, as you’ll see, we will), those fields are not included.

    We also need to add a new DbSet on our DbContext class at VehicleQuotes/Data/VehicleQuotesContext.cs so that Entity Framework picks it up as part of the data model:

    public DbSet<UserApiKey> UserApiKeys { get; set; }
    

    With that, let’s now create and run a migration so that the new table can be added to the database. All it takes is running this to create the migration:

    dotnet ef migrations add AddUserApiKeysTable
    

    And this to apply it:

    dotnet ef database update
    

    Now the new table should appear in the database:

    vehicle_quotes=# \dt
                        List of relations
     Schema |         Name          | Type  |     Owner      
    --------+-----------------------+-------+----------------
    ...
     public | user_api_keys         | table | vehicle_quotes
     (15 rows)
    
    The class that creates API Keys

    The next step is very straightforward. All we need is some logic that, when given a user account (in the form of an instance of IdentityUser), can generate a new API Key, insert it in the database, and return it. The following unremarkable class, defined in VehicleQuotes/Services/ApiKeyService.cs, encapsulates said logic:

    using System;
    using Microsoft.AspNetCore.Identity;
    using VehicleQuotes.Models;
    
    namespace VehicleQuotes.Services
    {
        public class ApiKeyService
        {
            private readonly VehicleQuotesContext _context;
    
            public ApiKeyService(VehicleQuotesContext context)
            {
                _context = context;
            }
    
            public UserApiKey CreateApiKey(IdentityUser user)
            {
                var newApiKey = new UserApiKey
                {
                    User = user,
                    Value = GenerateApiKeyValue()
                };
    
                _context.UserApiKeys.Add(newApiKey);
    
                _context.SaveChanges();
    
                return newApiKey;
            }
    
            private string GenerateApiKeyValue() =>
                $"{Guid.NewGuid().ToString()}-{Guid.NewGuid().ToString()}";
        }
    }
    

    As you can see, this class’s single public method, aptly named CreateApiKey, just takes the given user and creates a new UserApiKey that’s associated with it and saves it into the database. The key values themselves are just two GUIDs concatenated.

    This is an admittedly simplistic and not-all-that-secure method for generating the keys themselves. For simplicity, we’ve gone with this. In a production app however, it’d be better to use a true cryptographically secure string. Here’s an article that explains how to do that with .NET.

    Naturally, this class needs to be used elsewhere in the application, so let’s make it available for Dependency Injection by adding the following line to VehicleQuotes/Startup.cs’s ConfigureServices method.

    services.AddScoped<Services.ApiKeyService>();
    
    The action method that puts it all together

    Now let’s finally write a new action method on VehicleQuotes/Controllers/UsersController.cs so that there’s an endpoint that clients can call to get API Keys. Before adding the action method though, as usual, we need to declare the following dependency as a constructor parameter and hold it in an instance variable:

     public class UsersController : ControllerBase
     {
         private readonly UserManager<IdentityUser> _userManager;
         private readonly JwtService _jwtService;
    +    private readonly ApiKeyService _apiKeyService;
     
         public UsersController(
             UserManager<IdentityUser> userManager,
             JwtService jwtService,
    +        ApiKeyService apiKeyService 
         ) {
             _userManager = userManager;
             _jwtService = jwtService;
    +        _apiKeyService = apiKeyService;
         }}
    

    The action method itself would look like this:

    // POST: api/Users/ApiKey
    [HttpPost("ApiKey")]
    public async Task<ActionResult> CreateApiKey(AuthenticationRequest request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        var user = await _userManager.FindByNameAsync(request.UserName);
    
        if (user == null)
        {
            return BadRequest("Bad credentials");
        }
    
        var isPasswordValid = await _userManager.CheckPasswordAsync(user, request.Password);
    
        if (!isPasswordValid)
        {
            return BadRequest("Bad credentials");
        }
    
        var token = _apiKeyService.CreateApiKey(user);
    
        return Ok(token);
    }
    

    This method is very similar to the other one that generates JWTs. Painfully so. In fact the only difference is that this one calls the ApiKeyService’s CreateApiKey method instead of JwtService’s CreateToken, for obvious reasons.

    These two are ripe for some refactoring to eliminate duplication but we won’t do that here. Take a look at the finished project on GitHub to see a neat refactoring option.

    Anyway, you can now try a POST into api/Users/ApiKey, pass it a valid set of credentials, and you should see it respond with a brand new API Key:

    Successful response for authenticated request

    The key can also be found in the database:

    vehicle_quotes=# select id, SUBSTRING(value,1,13), user_id from user_api_keys;
     id |   substring   |               user_id                
    ----+---------------+--------------------------------------
      1 | efd2133b-cc4a | aaad07b4-f109-4255-8caa-185fd7694c72
    (1 row)
    

    Securing an endpoint with API Key authentication

    Now that users have a way of getting API Keys, we can start securing endpoints with that authentication scheme. In order to acomplish that, we need to take three steps:

    1. Implement a custom authentication handler that runs the authentication logic.
    2. Configure the new authentication handler, plugging it into ASP.NET Core’s auth mechanisms.
    3. Secure certain endpoints via the Authorization attribute, specifying the scheme that we want to use to do so.

    We’ll do that next.

    Implementing the API Key authentication handler

    Like we’ve touched on before, the “authentication handler” is a class that can plug into ASP.NET Core’s authentication middleware and can run the authentication logic for a given authentication scheme. In practical terms, it’s a class that inherits from Microsoft.AspNetCore.Authentication.AuthenticationHandler and implements the HandleAuthenticateAsync method. Ours will live in VehicleQuotes/Authentication/ApiKey/ApiKeyAuthenticationHandler.cs and it looks like this:

    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace VehicleQuotes.Authentication.ApiKey
    {
        class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
        {
            private const string API_KEY_HEADER = "Api-Key";
    
            private readonly VehicleQuotesContext _context;
    
            public ApiKeyAuthenticationHandler(
                IOptionsMonitor<AuthenticationSchemeOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock,
                VehicleQuotesContext context
            ) : base(options, logger, encoder, clock)
            {
                _context = context;
            }
    
            protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                if (!Request.Headers.ContainsKey(API_KEY_HEADER))
                {
                    return AuthenticateResult.Fail("Header Not Found.");
                }
    
                string apiKeyToValidate = Request.Headers[API_KEY_HEADER];
    
                var apiKey = await _context.UserApiKeys
                    .Include(uak => uak.User)
                    .SingleOrDefaultAsync(uak => uak.Value == apiKeyToValidate);
    
                if (apiKey == null)
                {
                    return AuthenticateResult.Fail("Invalid key.");
                }
    
                return AuthenticateResult.Success(CreateTicket(apiKey.User));
            }
    
            private AuthenticationTicket CreateTicket(IdentityUser user)
            {
                var claims = new[] {
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.Email, user.Email)
                };
    
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
    
                return ticket;
            }
        }
    }
    

    The first thing to note is how we’ve defined the base class: AuthenticationHandler<AuthenticationSchemeOptions>. AuthenticationHandler is a generic, and it allows us to control the type of options object that it accepts. In this case, we’ve used AuthenticationSchemeOptions, which is just a default one already provided by the framework. This is because we don’t really need any custom options.

    If we did, then we would define a new class that inherits from AuthenticationSchemeOptions, give it the new fields that we need, and use that as a generic time parameter instead. We would then be able to set values for these new fields during configuration in the app’s Startup class. Just like we’ve done with the “Jwt Bearer” auth scheme via the AddJwtBearer method.

    The next point of interest in this class is its constructor. The key aspect of it is that it calls base and passes it a series of parameters that it itself gets. This is because AuthenticationHandler does have some constructor logic that needs to be run for things to work properly, so we make sure to do that.

    Finally, there’s the HandleAuthenticateAsync method which does the actual authentication. This method inspects the incoming request looking for an “Api-Key” header. If it finds it, it takes its value and makes a query into the database to try and find a record in the user_api_keys table that matches the incoming value. If it finds that, it creates an AuthenticationTicket which contains the identity of the user that the key belongs to, and returns it wrapped in a AuthenticateResult.Success. That return value signals ASP.NET Core’s authentication middleware that the request is authentic. Otherwise, it returns AuthenticateResult.Fail, which prompts ASP.NET Core to halt the request and return a 401.

    Something interesting to note is how the AuthenticationTicket, similarly to the JWT Bearer scheme’s JwtSecurityToken, also includes an array of “claims” that serve to identify the user that has been authenticated. The “claims” concept is central to how auth works in ASP.NET Core.

    Enabling and configuring the API Key authentication

    Now we need to tell the framework that we want it to use a new authentication scheme for our app, spearheaded by the custom authentication handler that we just wrote. There are two steps to make that happen.

    First, we configure our new scheme in the app’s Startup class. For that, we need these two new using statements:

    using Microsoft.AspNetCore.Authentication;
    using VehicleQuotes.Authentication.ApiKey;
    

    Then we add this right after the AddJwtBearer call in the ConfigureServices method:

     public void ConfigureServices(IServiceCollection services)
     {
         // ...
         services
             .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
             .AddJwtBearer(options =>
             {
                 options.TokenValidationParameters = new TokenValidationParameters()
                 {
                     ValidateIssuer = true,
                     ValidateAudience = true,
                     ValidateLifetime = true,
                     ValidateIssuerSigningKey = true,
                     ValidAudience = Configuration["Jwt:Audience"],
                     ValidIssuer = Configuration["Jwt:Issuer"],
                     IssuerSigningKey = new SymmetricSecurityKey(
                         Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])
                     )
                 };
             })
    +        .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>(
    +            "ApiKey",
    +            options => { }
    +        );
     }
    

    A simple configuration as far as auth schemes go. We specify both the types of the authentication handler and the options object that it accepts via the generic type parameters to the AddScheme method. "ApiKey" is just the name we’ve given our custom auth scheme. It can be anything as long as it’s not “Bearer”, which is the name of the Jwt Bearer scheme (which is the value returned by JwtBearerDefaults.AuthenticationScheme). We’ll refer back to it later. Finally, since we have no special options to give it, we specify a lambda that does nothing with its options.

    Updating the Authorize attribute to use our two schemes

    Now that the auth scheme is configured, we need to use it to secure an endpoint. We can use the same that we used for JWT: POST api/BodyTypes, defined in BodyTypesController’s GetBodyTypes action method. Remember though, that we wanted to have both auth schemes (JWT and API Key) work on the endpoint. For that, the Authorize attribute allows a comma separated string of scheme names as a parameter. So, we can get both our configured schemes working if we update the attribute like this:

    [Authorize(AuthenticationSchemes = $"{JwtBearerDefaults.AuthenticationScheme},ApiKey")]
    public async Task<ActionResult<IEnumerable<BodyType>>> GetBodyTypes()
    {
        return await _context.BodyTypes.ToListAsync();
    }
    

    JwtBearerDefaults.AuthenticationScheme contains the name of the JWT Bearer auth scheme. Next to it, we just put “ApiKey”, which is the name we have given our new custom one.

    It’d be nice to put that “ApiKey” string somewhere safe similar to how the “Jwt Bearer” scheme has it in a constant. Take a look at the finished project on GitHub to see one way to organize that and also how to make the Startup configuration a little less verbose by using extension methods.

    And finally, let’s test our endpoint and marvel at the fruit of our work.

    Make sure to create a new API Key by POSTing to api/Users/ApiKey, and copy the “value” from the response. We can use it as the value for the Api-Key header a GET request to api/BodyTypes. In Postman, we can do that by choosing the “Authorization” tab, selecting “API Key” in the “Type” drop-down list, writing “Api-Key” in the “Key” text box, and putting our key in the “Value” text box.

    With that, we can make the request and see how the endpoint now allows authentication via API Keys:

    Successful response for request authenticated with API Key

    Of course, requests authenticated via JWT will also work for this endpoint.

    That’s all

    And there you have it! We’ve updated an existing ASP.NET Core Web API application so that it supports authentication using two strategies: JWT and API Keys. We leveraged the Identity libraries to securely store and manage user accounts. We used ASP.NET Core’s built-in authentication capabilities to enable JWT generation and usage. For API Keys, the framework didn’t provide an implementation out of the box. However, it proved to be extensible enough so that we could implement our own.

    Table of contents


    authentication security dotnet aspdotnet

    3 useful built-in objects and functions in JavaScript

    Phineas Jensen

    By Phineas Jensen
    June 15, 2022

    Three semi trucks parked on concrete, with a dog standing watch over them

    I love learning (and learning about) programming languages. Right now I’m teaching myself Rust and trying to learn about 3D rendering. Every time I find a list of programming languages I have to look up every one that I haven’t heard of, and when I see posts about Zig or Haskell or any other cool project on Hacker News, I can’t help reading through the comments and seeing people discuss the unique features, quirks, and drawbacks of each language.

    One thing I enjoy about learning about these languages (sometimes in an all-too-shallow way) is seeing all the different methods and syntaxes that exist for solving problems, and while it’s always tempting to think the grass is greener on the other side, it’s also important to do the same kind of exploration within the languages I’m using right now. Not only is it important, it actually makes using those languages a lot more enjoyable as I find new, more efficient ways to do things I’ve probably done dozens of times before.

    With that spirit, here’s a little list of cool objects and functions in JavaScript that have given me that feeling of excitement and made the language more fun and satisfying to use. Like any JavaScript feature, support will vary from browser to browser, runtime to runtime, and version to version, but with tools like Webpack becoming ubiquitous, that’s becoming less of a problem.

    The Set object

    The Set object has a lot of use cases, but one I find myself using a lot is a set of selected items in a list like this:

    A table of items which can be selected with check boxes

    Why is Set a good fit for this? Because a Set is a list of unique items (we shouldn’t have one item in the selected list twice), we can add and remove items from it easily, and we can check membership with a single, obvious method that returns a boolean. Let’s look at an example using an array first, which mimics code I’ve seen quite a bit.

    const selected = [];
    
    function select(item) {
        if (selected.indexOf(item) == -1) {
            selected.push(item);
        }
    }
    
    function deselect(item) {
        const index = selected.indexOf(item);
        if (index != -1) {
            selected.splice(index, 1);
        }
    }
    
    function isSelected(item) {
        return selected.indexOf(item) != -1;
    }
    

    This isn’t too complex, and it works well, but it’s pretty verbose. Using the indexOf function three times is a little messy, and the splice function is uncommon enough that it’s easy to forget exactly how it works. We can implement the same functions more simply using a Set:

    const selected = new Set();
    
    function select(item) {
        selected.add(item);
    }
    
    function deselect(item) {
        selected.delete(item);
    }
    
    function isSelected(item) {
        return selected.has(item);
    }
    

    In fact, I’d say this functionality is so much simpler that we don’t even need to define new functions. Selecting an item by calling selected.add(...) reads just as well as or better than select(...), and the same goes for these other function names. Set handles it all for us.

    Note that if you use a Set in React with useState, the component won’t re-render unless you create a new Set object when you update the state, like so:

    function Example(props) {
        const [selected, setSelected] = useState();
        
        const addItem = (item) => {
            selected.add(item);
            setSelected(new Set(selected));
        }
    
        return items.map(item => (
            <button onClick={() => addItem(item)}>
        ));
    }
    

    This is because React will bail out of the state update if the values are the same according to the Object.is function.

    The URL API’s URLSearchParams object

    Imagine you’re writing a function that needs to parse and use a URL’s search parameters. Pretty simple task, right? It wouldn’t be too hard to do by hand, but there’s probably a library out there somewhere, so you do a Google search for “javascript parse url parameters” and click on the first Stack Overflow answer in the results. You’re greeted with an answer that defines a function using .split() a few times and then a much longer and more complex function that decodes the parameters in a non-standard way. Is this really necessary?

    With the built-in URLSearchParams object, no. Supported in every major browser except Internet Explorer (which just reached its end of support life), this nice object handles parsing and formatting of string parameters with proper encoding and decoding:

    const params = new URLSearchParams('?foo=1');
    
    params.append('bar', 'has weird & tricky characters'); // Add another parameter
    console.log(params.toString()); // Prints 'foo=1&bar=has+weird+%26+tricky+characters'
    

    It handles multiple parameters with the same key easily and supports iteration:

    const params = new URLSearchParams('?foo=1&foo=2&foo=3');
    console.log(params.getAll('foo')) // Prints ["1", "2", "3"]
    
    for (const [k, v] of params) {
        console.log(`${k}: ${v}`);
    }
    // Output: 
    // foo: 1
    // foo: 2
    // foo: 3
    

    Much nicer, and requires no copy-pasting of code from Stack Overflow or installing dependencies!

    The Array iterator functions

    If you’ve used a lot of JavaScript or used languages like Perl, Python, Ruby, Rust, functional languages, or languages with iterators, you’ve probably seen functions like map or forEach. I’ve used them pretty extensively but still find that a lot of people don’t seem to know about them. For people coming from languages like C or C++, where they aren’t available, or for people who are fresh out of a university program where neat things like that are often not taught in favor of theory, that’s not much of a surprise.

    Array.prototype.forEach is pretty straightforward. Compare the following equivalent code snippets:

    const names = ["bob", "roberta", "alice", "reza"];
    
    // This...
    for (let i = 0; i < names.length; i++) {
        console.log(names[i].substr(0,1).toUpperCase() + names[i].substr(1));
    }
    
    // ...is equivalent to this:
    names.forEach(value => console.log(value.substr(0,1).toUpperCase() + value.substr(1)));
    

    forEach takes as its argument a callback function, which it then calls once for each item in the array. The callback function can take more arguments than just the value as well; see its MDN page.

    I personally find map more interesting and useful. Map is very similar to forEach, except that the values returned by the callback function are assembled to make a new array of the same length as the old one, essentially returning a transformed array.

    const names = ["bob", "roberta", "alice", "reza"];
    
    // This...
    const uppercaseNames = [];
    for (let i = 0; i < names.length; i++) {
        const name = names[i];
        uppercaseNames.push(name.substr(0,1).toUpperCase() + name.substr(1));
    }
    
    // ...is equivalent to this:
    const uppercaseNames = names.map(name => name.substr(0,1).toUpperCase() + name.substr(1));
    

    There are similar cool functions that return true or false if the callback function returns true for every or some item in the list:

    const names = ["bob", "roberta", "alice", "reza", "spaces are good"];
    // Both values will be false because of the "spaces are good" item
    const allSpaceless = names.every(name => name.indexOf(" ") === -1);
    // or...
    const allSpaceless = !names.some(name => name.indexOf(" ") !== -1);
    

    Or we can find individual or multiple items using a function, rather than just by value:

    const names = ["bob", "roberta", "alice", "Reza", "spaces are good"];
    const namesStartingWithR = names.filter(name => name[0].toLowerCase() === "r") // returns ["roberta", "Reza"]
    const firstNameWithC = names.find(name => name.toLowerCase().includes("c")) // returns "alice"
    

    Then there’s the complex but very cool reduce function, which starts with an initial value and replaces it with the result of the callback function on each iteration. map can be implemented using reduce:

    const names = ["bob", "roberta", "alice", "reza"];
    // This...
    const uppercaseNames = names.map(name => name.substr(0,1).toUpperCase() + name.substr(1));
    // ...is equivalent to this:
    const uppercaseNames = names.reduce(
        (previousValue, name) => {
            previousValue.push(name.substr(0,1).toUpperCase() + name.substr(1));
            return previousValue;
        },
        [] // Initial value
    );
    

    Obviously, it’s not terribly useful in this case, but can be extremely useful for things like summing items in a list or transforming a list into an object somehow.

    Consider keeping the Array reference on hand to keep these functions close to your heart and start to learn some of their other features. As new versions of ECMAScript come out, new prototype functions will crop up too. Note that some of these functions will vary in performance compared to normal loop iterations. If you’re using them in heavy workload or low-latency scenarios, make sure to compare and benchmark performance as needed.

    Conclusion

    Okay, that was actually more than 3 things, but they fit into three categories, so I’m keeping the post’s name. JavaScript’s built-in objects and the generally available Web APIs have a lot more cool APIs and functions and features that are very much worth checking out. MDN (which I linked several times throughout this article) is a fantastic resource to learn about them, so make sure to get familiar with it as you work with JavaScript. It’ll pay off.


    javascript tips
    Previous page • Page 2 of 206 • Next page