Tuesday, July 21, 2020

Test node js npm package in browser with parcel bundle

Usually node js app runs under node js environment, and testing a npm package within a console node app is inconvenient. It would be easier if npm package can be tested easily from browser as a web app.

Parcel bundle from https://parceljs.org/ can convert a node js app into a regular web app, so as to test and debug a npm package from browser. The below steps show how to test the npm package https://www.npmjs.com/package/financejs from browser.

1. run 
npm install -g parcel-bundler
to install parcel bundle

2. run
npm init
to initialize npm package file.

3. Add browserslist configuration in generated package.json to specify the allowed browsers
{
  "name": "npmtest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
"browserslist": [ "last 1 Edge version", "last 1 Chrome version", "last 1 Firefox version" ],
  "license": "ISC",
  "dependencies": {
    "financejs": "^4.1.0"
  }
}

 
4. create index.html with necessary ui element as below
<!-- index.html -->
<!DOCTYPE html>
<html>

<body>
    <input type="text" id="input"/>
    <button id="test">test</button>
    <p><b>Status:</b></p>
    <p id="status" style="height:160px; width: 593px; overflow: scroll;" />
  </body>

<script src="./index.js"></script>
</html>

5. create index.js using regular node js code as below. Basically, it imports financejs package, and then call CAGR method to get the required rate in order to double the amount in the specified years input by user.

const input = document.getElementById("input");
const testButton = document.getElementById("test");
const status = document.getElementById("status");

testButton.addEventListener("click", onclick);
const reportStatus = message => {
    status.innerHTML += `${message}<br/>`;
    status.scrollTop = status.scrollHeight;
}

var Finance = require('financejs');
var finance = new Finance();

function onclick() {
    var data = input.value;
    // To calculate Amortization
    let rate =  finance.CAGR(10000, 20000, data);
    reportStatus("rate is: " + rate);
}

6. From Windows command line tool or visual studio code terminal, run the below command to start the web app
parcel index.html
Based on the port number shows in console output, open browser to localhost:xxxx to debug the npm package from browser js debugger.

Sunday, July 19, 2020

Angular connects to Azure storage account through Spring boot service API

In regular web architecture, angular (front end), spring boot (api service), and backend data storage service work to together to handle the client request. 
For azure deployment, one common task is connecting spring boot project to azure data storage and database service (sql or no sql). As spring boot project is just a regular java project, so the way how to access azure service from java sdk applies to the spring boot project. The below steps describe how to connect spring boot app (i.e. java sdk project) to azure storage account service.

(Note, spring-azure-starter-storage or azure-storage-spring-boot-starter has been deprecated by Microsoft, so do not use it anymore)  

1. add below azure-storage-blob dependency module in spring boot pom file
    <!-- https://mvnrepository.com/artifact/com.azure/azure-storage-blob -->
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-storage-blob</artifactId>
        <version>12.7.0</version>
    </dependency>

2. in Azure storage account settings, select the container's items and be sure the access level is set to private (no anonymous access), so that those blob can only be accessed by spring boot project. You can verify this by accessing the blob url from browser, and you should get a resource not found error. Copy the access key and container name for later use.

3. azure-storage-blob provides few helper classes to access the data from azure container account, please refer https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-java
for more details.
In constructor, create a containerClient instanceas below
    public StorageService(){
        this.blobServiceClient = new BlobServiceClientBuilder().connectionString(connecString).buildClient();
        this.blobContainerClient = this.blobServiceClient.getBlobContainerClient(containerName);
    }

3.1 upload a file to container
In RestController class, create a method to handle client side angular web app's http request
    @PostMapping("/storages")
    public void createBlob(@RequestBody Map<String, Object> inputData) throws IOException {
        String name = (String) inputData.get("name");
        String data = (String) inputData.get("data");
        service.uploadBlob(name, data);
    }

Create a java function in service class to upload a blob on BlobClient object as below
    public void uploadBlob(String name, String strData) throws IOException {
        BlobClient blobClient = this.blobContainerClient.getBlobClient(name);
        InputStream targetStream = new ByteArrayInputStream(strData.getBytes());
        blobClient.upload(targetStream, targetStream.available(), true);
    }

From client side angular app, a xhr http request will be sent to spring boot app when user
clicks a button on html page:
  onCreateStorage() {
      const data = {
        name: this.nameField.value,
        data: this.dataField.value,
      };

      this.http.post(this.serviceUrl + '/storages/', data).subscribe((resp) => {
        this.resultField.setValue(resp);
      });
    }

In similar way, the other operations (like download, delete etc) can also be implemented.

Saturday, July 11, 2020

OAuth2 and OpenID difference regarding JWT and token inspection endpoint

OAuth2 is a popular framework for authenticating user with third party identity provider (authentication server). Basically user first authenticates to the third party IDP to get grant code, and then the client or web app can request the access token with grant code from authorization server. Usually authentication server and authorization server are from the save third party id provider.

For OAuth2, access token is sent by the client app to resource server when the client requests the data from resource server, however, the oAuth2 does not specify the content or format of the access token. From resource server's point of view, it is just a simple string without any special information for the client who sends the request. So the resource server must contact authorization server to validate the access token in order to find out whether it can give the permission to the client request. This is why oAuth2 authorization server needs to expose a token introspection endpoint url for resource server to validate the token. 

With Oauth2 token introspection endpoint, OAuth2 authorization server can support single sign off, as authorization server can invalidate an issued token at anytime, and return failure for all following token validation requests sent from resource server.

However, it is not very efficient if resource server needs to send a token validation request to authorization for every client side resource request. That is what has been improved by OpenID. which is built on top of OAuth2.

OpenID introduces JWT (json web token) to represent the token format, so it includes all the required information that resource server needs to know in order to decide whether it should accept the client request or not, so there is no need to send an extra request to token introspection server for this purpose.

JWT is a based 64 encoded json string containing user id information, grant scope, token expiration time, and signing algorithm, and signing signature. Note the information included in token are not confidential, so anyone can read it. However, since the token is digit signed with authorization server's private key, can be easily verified by anyone using the authorization server's public key, so the token cannot be changed by someone for malicious purpose. 

JWT has significantly simplified the token validation logic, as the authorization server can just create a token and then digit sign it, and forget about it. Anyone gets the token can validate the token by just using the authorization server's public key, without actually communicating with authorization server. One drawback of OpenID is there is no simple way to support single sign off, as once a token is singed and sent out by authorization server, it will be always accepted by resource server until the token is expired.

 


Wednesday, July 8, 2020

Deploy Angular and spring boot apps to Azure app service or storage account

1. Deploy angular web app as static html web app
One typical web project structure is Angular (front SPA web app) communicating with backend Spring boot (API function app), and deployed to Azure cloud. The app can use Azure Active Directory or Azure B2C for user authentication.

To demo the various options for this architecture, a simple prototype web application will be created and deployed to Azure.

1. Create Angular SPA app with git repository
Create a new angular app with two button, one for login and one for sending a request to service api app. For now both button just logs a console log.
Add the project into a new git repository, so it can be deployed to azure from git using the Azure Static Web App service. The git repository is at https://github.com/lijo2/angularspa

2. Deploy the angular project to Azure 

2.1 Option 1 - Host static html web site with app service
2.1.1 First create an app service plan , for example, haiquanserviceplan, and a resource group, for example, angularwebapp. 

2.1.2 Second, from azure portal, find the storage account created by Azure for supporting cloud shell bash, the storage account name starts with "cs". In storage account's file service, create a new directory, and copy the angular's production build's dist folder to this new directory. In this example, all html and js files are copied to a directory called "spa" in the storage account.
Note, the azure cloud shell only provides function to upload/download a single file, so it cannot be used to upload a folder in this case.

2.1.3 open azure cloud shell from azure portal, and cd into the new directory just created in 2.2, the folder should contain the html and js files used by the static web app, then run the below command to create the static web app
az webapp up --location eastus --name spawebappli --html -p haiquanserviceplan -g azuretest

The above command will create a new web app containing static html and js resource in the resource group, if later, you need to update the resource or include some sub folder, then you can send the web app resource as a zip file by using az webapp deployment source config-zip command, or using a FTP client. 
az webapp deployment source config-zip -g azuretest -n spawebappli --src app.zip
-g: resourcegroup name
-n: app name

Note for creating zip file with npm command:
npm package 'npm-build-zip' can be installed and automatically zip the dist folder to generate a zip file with npm
 npm install npm-build-zip

Update the scripts in package.json to automatically generate the zip file and deploy to azure app service.
    "zip": "npm-build-zip --source=dist/angularspa --destination=dist",
    "deploy": "az webapp deployment source config-zip -g azuretest -n spawebappli --src dist/angularspa_0.0.0.zip"
 
To create a new deployment, run from visual studio code terminal:
npm run zip
npm run deploy

2.1.4 browser to the new app's url to verify the html web app has been created properly

2.1.5 Using FTP to update and upload/download web resource
Azure web app allows FTP access to the web application's site/wwwroot/folder. For this purpose, you will need to first install FTP client (for example, WinSCP). The ftp url and username/password are available in Azure portal's app service's deployment/deployment Center/FTP page.
 
2.2 Option 2 - host static web site in Azure storage account
2.2.1 Create a storage account 
2.2.2 In storage account's Setting/Static Website blade, enable static website
2.2.3 Download and install Azure Storage Explorer to upload the static website's resource files from local to storage account's $web container as shown below. You can also do so with azcopy from command line. Azure Storage Explorer can easily create sub folder, and upload files to sub folder.
2.2.4 Test the static web site from the url shown in storage account's Setting/Static Website blade
3. Deploy spring boot app to azure app service

As azure already created a maven plugin for web app, so it can be used to deploy the spring boot project to azure as web app.

3.1 create and test the spring boot project in localhost with 
mvn spring-boot:run

3.2 run  to create the jar file
mvn package

3.3 run 
mvn com.microsoft.azure:azure-webapp-maven-plugin:1.9.0:config
to config azure maven web app plugin by entering the appname, service plan, pricingtier and other information.

3.4 update pom file's azure mvn plugin setting to specify a port number 
   <plugin> 
        <groupId>com.microsoft.azure</groupId>  
        <artifactId>azure-webapp-maven-plugin</artifactId>  
        <version>1.9.0</version>  
        <configuration> 
          <schemaVersion>V2</schemaVersion>  
          <resourceGroup>azuretestspring</resourceGroup>  
          <appName>springapitestli</appName>  
          <pricingTier>B1</pricingTier>  
          <region>eastus</region>  
          <runtime> 
            <os>linux</os>  
            <javaVersion>java11</javaVersion>  
            <webContainer>java11</webContainer> 
          </runtime>  
          <appSettings> 
            <property> 
              <name>JAVA_OPTS</name>  
              <value>-Dserver.port=80</value> 
            </property> 
          </appSettings>  
          <deployment> 
            <resources> 
              <resource> 
                <directory>${project.basedir}/target</directory>  
                <includes> 
                  <include>*.jar</include> 
                </includes> 
              </resource> 
            </resources> 
          </deployment> 
        </configuration> 
      </plugin> 
3.5 run
mvn azure-webapp:deploy
to deploy the web app to azure app service plan.

3.6 after the spring boot app is updated, you will need to run
mvn package
mvn azure-webapp:deploy
to update the azure's deployment.

4. Send request from Angular project to sprint boot project
After deploying both angular and spring boot project to azure, the next step is enabling angular project to send xmlhttprequest to spring boot project. The following example shows how to send simple xhr request when a button is clicked in Angular project
  onClick() {
    console.log('call backend api to get data');
    const str = this.textField.value;
    this.http.get('https://springapitestli.azurewebsites.net/jsonapi/' + str).subscribe((resp) => {
      this.textField.setValue(JSON.stringify(resp));
    });
  }
Note, as angular project and spring boot project are deployed to different root url in azure, so sending xhr request from angular project to spring project will fail by default due to CORS. To make it work, the azure app service for spring boot project must add the angular app's url to its allowed CORS url. Alternatively, spring boot annotation of @ can also be used to specify allowed CORS origin headers.