Healthkit -> Table of Contents:
Healthkit Notes
What you're looking at is an example of the approach I take anytime I'm tasked with either building something new or repairing an already working application that I'm not familiar with. Whatever skillset you're bringing to the table, popping the hood on what you're trying to learn - be it a new language or just the infrastructure of an app built in a format you're familiar with - in order to get to that place where you truly understand what you're doing, you need to position yourself as a "note taking" student. This is what I look like as a "note taking" student having been tasked with building an interface using OAuth to access a user's Fitbit information. Starting with defining "Healthkit" and then several terms that are used to describe OAuth and Open Api, you'll then see my research into "Spotlight" and "Swagger" as part of learning how to craft an OpenAPI document. At "IX," you'll see how I popped the hood on the Boilerplate our team was asked to use and then we get into the actual process of OAuth authentication. XIII | B features notes from a youtube tutorial and then... Letter C features the final, working code with certain elements intentionally omitted for the sake of security. A) iOS B) Healthkit A) oauth B) Open API C) Markdown D) Single Source of Truth E) State F) Microservices G) Containerization H) IDE I) Modeling J) enum K) artifact L) OAS M) CRUD N) contract O) ETL P) Datapoint Q) Boilerplate R) Endpoint S) YAML A) Definition / Description B) Open API Specification A) Definition / Description B) Resources A) Definition / Description B) What's the Difference Between Swagger and Stoplight C) Swagger Petstore Example 1) CURL D) Create and Edit Your Open API on Swagger 1) Petstore Example 2) The Swagger v3 Contract Here's a great web resource that documents any one of a number of real world examples of working OpenAPI contracts. A) Apicurio B) Working Example A) json_decode B) json_encode A) Terms and Technologies B) JSON WebToken Authentication A) Definition A) Stoplight 1) Nomenclature a) Request Parameters b) Responses c) Send a Test Request i) Settings ii) Query iii) Code Generation A) Overview A) Working OpenAPI Contract 1) Swagger Version 2) Stoplight JSON Version B) JWT / Node Tutorial C) Actual Authentication 1) Working Code 1) user.js 2) app.js 3) Setting Up a User 4) Finding the User D) Deployment E) Authentication w/ Database
A) iOS (back to top...) "iOS" is Mac's operating system. You can click here to read more about the "i" that's so popular with Mac's branding format. B) Healthkit (back to top...) "Healthkit" is an app made by Mac that gathers all of your health related information that's being collected by your iWatch or your iPhone. From the standpoint of AHA, you used to be able to access an individual's FitBit directly. Now, with Apple, you have to interface with "Healthkit" in order to access that information. We're going to do that with an application built in Node... A) oauth (back to top...) OAuth (Open Authorization) is an open standard for token-based authentication and authorization on the Internet. OAuth, which is pronounced "oh-auth," allows an end user's account information to be used by third-party services, such as Facebook, without exposing the user's password. OAuth acts as an intermediary on behalf of the end user, providing the service with an access token that authorizes specific account information to be shared. The process for obtaining the token is called a flow. B) Open API (back to top...) An API is an Application Programming Interface that allows two applications to talk to one another. The user doesn't see it. One page is sending requests to a database and the API facilitates the response to those requests without the page the user is on to every change or be refreshed. An Open API is one that is available to the public. Generally an API is going to be something that's restricted to a particular company and you need to be validated in order to interact with it. An Open API is going to be available to the general public. C) Markdown (back to top...) "Markdown" is a lightweight markup language with plain text formatting syntax. Its design allows it to be converted to many output formats, but the original tool by the same name only supports HTML. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. D) Single Source of Truth (back to top...) The values coming from your page are generally coming from your DOM (Document Object Model). In other words, from your form fields. It is possible, though, that a value can be stored in JavaScript and in that instance you've got two "sources of truth." The "Single Source of Truth" is the data that's coming from the user, usually in the context of a field they've manually entered data in, as opposed to a potentially conflicting piece of data coming from JavaScript or something similar. E) State (back to top...) In more "normal" programming languages, the "state" of the program is all its global variables, static variables, objects allocated on the heap, objects allocated on the stack, registers, open file descriptors and file offsets, open network sockets and associated kernel buffers, and so forth (stackoverflow). For the sake of our API, the "state," then, is going to be the position our user / page is in that's poised to deliver data. F) Microservices (back to top...) A Microservice is like a piece of a puzzle, only it does something all by itself. Here's the definition from Wikepedia: Microservices are a software development technique—a variant of the service-oriented architecture (SOA) architectural style that structures an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight. The benefit of decomposing an application into different smaller services is that it improves modularity. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion.[1] It parallelizes development by enabling small autonomous teams to develop, deploy and scale their respective services independently.[2] It also allows the architecture of an individual service to emerge through continuous refactoring.[3] Microservice-based architectures enable continuous delivery and deployment. It's a piece of a greater whole. G) Containerization (back to top...) Containerization is similiar to Microservices, but they're not tied to any one computer or any application. Click here to read more about the differences. H) IDE (back to top...) IDE means "Integrated Development Environment." I) API Modeling (back to top...) API Modeling is a process where you're breaking down what your API needs to accomplish. Here's the definition from the "Haufegroup..." API modeling consists of 5 activities that help identify the requirements of your API design: 1) Identify the participants, or actors, that will interact with your API 2) Identify the activities that participants wish to achieve 3) Separate the activities into steps that the participants will perform 4) Create a list of API methods from the steps, grouped into common resource groups 5) Validate the API by using scenarios to test the completeness of the API The goal of each step is to explore the requirements of the API from a variety of different perspectives: those involved (the participants), what they want to do (the activities), and how they will complete an activity (the steps). The modeling process will be iterative, so it should be expected that each modeling activity may require that you revisit the previous step as previously hidden details are exposed. J) enum (back to top...) "enum" is short for "enumerate" and it is used to restrict a value to a fixed set of values. It must be an array with at least one element, where each element is unique. The following is an example for validating street light colors:
{ "type": "string", "enum": ["red", "amber", "green"] } "red" -> works "blue" -> won't be accepted
K) artifact (back to top...) "Artifact" is just a fancy word for "documentation." Click here for more info. L) OAS (back to top...) OAS stands for "Open API Specification." It's referring to the standard used to create your "artifact." M) CRUD (back to top...) Acrostic that means "Create Read Update & Delete." The four main functions of a database. N) contract (back to top...) An API contract is a document that is an agreement between different teams for how the API is designed. The most common form of an API contract today is an OpenAPI Specification (formerly known as Swagger). OAS is an vender neutral, portable, and open API description format, which standardizes how REST APIs are described. The question of “who should create it” can be a tricky one. I work at Stoplight, where I help lots of people build out an OAS file, so I see a lot of different ways. Best practice is that it is either built by committee (i.e. architectural council, group of reviewers) or by an API or software architect. It really depends on the organization. In many cases where the team is smaller, it is just a couple of developers agreeing on different aspects of the API design. The most important thing is that everyone agrees that what is in the specification or contract is the agreed upon design of the API (from quora). O) ETL (back to top...) From webopedia: ETL is short for extract, transform, load, three database functions that are combined into one tool to pull data out of one database and place it into another database. Extract is the process of reading data from a database. In this stage, the data is collected, often from multiple and different types of sources. Transform is the process of converting the extracted data from its previous form into the form it needs to be in so that it can be placed into another database. Transformation occurs by using rules or lookup tables or by combining the data with other data. Load is the process of writing the data into the target database. P) Datapoint (back to top...) A data point is a discrete unit of information. In a general sense, any single fact is a data point. In a statistical or analytical context, a data point is usually derived from a measurement or research and can be represented numerically and/or graphically. The term data point is roughly equivalent to datum, the singular form of data. (whatis.com). Q) Boilerplate (back to top...) Boilerplate is the term used to describe sections of code that can be included in many places with little or no alteration. It is more often used when referring to languages which are considered verbose, i.e. the programmer must write a lot of code to do minimal jobs. Boilerplate code is a piece of code which can be used over and over again or we can say it, a piece of reusable code. (medium.com). Once you have everything downloaded, you're going to need to run and install "Yarn." It's a little confusing at first because it would appear as though it was something that came with the Boilerplate package. But if you go with just that, you'll get an error that the "yarn" is an unrecognized command. You have to install Yarn on your machine and you can do that by heading out to https://yarnpkg.com/lang/en/docs/install/#windows-stable. bash bin/development.sh R) Endpoint (back to top...) ...today endpoint is used most commonly in network security and end user mobility circles to mean any device outside the corporate firewall. That could be a laptop, tablet, or mobile phone on the “edge” (or periphery) of the network and which individuals connect to the central network (from Druva). S) YAML (back to top...) YAML is the format you'll use to document your OpenAPI. It stands for "YAML ain't markup language." A) Definition / Description (back to top...) The best way to describe an API is to envision a page that would list a bunch of things, but without the GUI. So you have the info, but no HTML or CSS. Think of it this way: In the past, if you wanted to list all of the employees in an organization, you would have a page that had some backend code on it that would make a call to the database via a query that would grab all of the names from a database. Then with the appropriate HTML and CSS you would create the necessary scaffolding to display the results coming from the database. An API takes a different approach in that it's not at all concerned about the visual aspect of that list of employees. Rather, it focuses exclusively on the data and instead of being something that is limited to a specific URL, it's set up to be triggered by a RESTful dynamic so it can be accessed potentially by multiple domains. B) Open API Specification (back to top...) An API is distinct from Spotlight and things like Swagger etc. The API is the code, Spotlight is your artifact and testing facility. You're using these tools to spec your API in a way that both users and developers can understand and implement into their own system. A) Definition / Description (back to top...) First thing:
IT'S NOT CODE!
OAS, Stoplight and Swagger are tools and testing enviornments that are code agnostic. You'll write you code later, the one caveat being that you'll be sending and receiving your data in the context of JSON. This is a tool used to generated API documentation based on OAS 3.0 specifications. "OAS" stands for "Open API Specification." We've said that more than once, but it bears repeating...! If you're inclined to think that an artifact or a contract may not be needful, consider the graphic below:
You can see why you want to pause for a moment and engage the Stoplight dynamic and spec out your API according to the contract that exists between you, your GUI crew and your end user. B) Resources (back to top...) Here are some useful Stoplight tutorials and docs: A) Definition / Description (back to top...) Swagger is a resource all by itself, but it's also often used to refer to OAS version 2. That can make things confusing, but if you can keep those two things in mind, you'll be able to successfully navigate most of the conversations that mention Swagger in either of those two scenarios. B) What's the Difference Between Swagger and Stoplight (back to top...) What's the difference between Swagger and Stoplight? This is coming from stackshare... StopLight vs Swagger UI: What are the differences? StopLight: Visual API Tooling. Stop writing thousands of lines of specification code. Our intuitive visual editors significantly cut down on design time, and are spec agnostic. Generate OAI (Swagger) and RAML specification code on demand; Swagger UI: dependency-free collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation. Swagger UI is a dependency-free collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation and sandbox from a Swagger-compliant API. Bottom line: With Swagger, you're getting a service that aids in actually generating documentation. Spotlight doesn't do that. C) Swagger Petstore Example (back to top...)
Most of what you're seeing from this point on that pertains to "Swagger" is coming from a series of videos that you can access by clicking here
You'll run across PATCH and PUT when you're looking at potential CURL commands. PATCH is sending some instructions to modify an already existing resource. PUT is sending the actual resource in a modified format (click here for more information.
Swagger Petstore is a sample of a fully armed and operational Open SPI contract that you can you run and play with to get your feet wet. For every end point, there's a corresponding "Try it Out" button. To see the interface that allows you edit that sample - which is a great way to begin with a solid and accurate starting point - head out to the editor which you can access by clicking here. 1) CURL (back to top...) Curl transfers information to data to URLs. You can use Postman, but you can also use CURL which stands for "Client URL." The bottom line is that when POST, GET, DELETE or UPDATE are utilized, you're sending info to a URL which will respond according to the database that's positioned behind that URL. When you're testing your Open API's, you're not necessarily using a database. To mimic that scenario, you'll use something like Postman or...Curl. Here's a video that you can watch that gives you a two minute intro to Curl. When you click on the "Try it Out" button, you'll see the CURL request, the Request URL and the Server Response. D) Create and Edit Your Open API on Swagger (back to top...) To create / edit your Open API on Swagger, you're going to go out to http://editor.swagger.io/A couple of terms right out of the chute: 1) Petstore Example (back to top...) Here's the original "Petstore" example:
swagger: "2.0" info: description: "This isn't a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: "1.0.0" title: "Swagger Petstore" termsOfService: "http://swagger.io/terms/" contact: email: "apiteam@swagger.io" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" host: "petstore.swagger.io" basePath: "/v2" tags: - name: "pet" description: "Everything about your Pets" externalDocs: description: "Find out more" url: "http://swagger.io" - name: "store" description: "Access to Petstore orders" - name: "user" description: "Operations about user" externalDocs: description: "Find out more about our store" url: "http://swagger.io" schemes: - "https" - "http" paths: /pet: post: tags: - "pet" summary: "Add a new pet to the store" // self explanatory description: "" // self explanatory operationId: "addPet" // self explanatory consumes: - "application/json" - "application/xml" produces: - "application/xml" - "application/json" parameters: - in: "body" name: "body" description: "Pet object that needs to be added to the store" required: true schema: $ref: "#/definitions/Pet" responses: 405: description: "Invalid input" security: - petstore_auth: - "write:pets" - "read:pets" put: tags: - "pet" summary: "Update an existing pet" description: "" operationId: "updatePet" consumes: - "application/json" - "application/xml" produces: - "application/xml" - "application/json" parameters: - in: "body" name: "body" description: "Pet object that needs to be added to the store" required: true schema: $ref: "#/definitions/Pet" responses: 400: description: "Invalid ID supplied" 404: description: "Pet not found" 405: description: "Validation exception" security: - petstore_auth: - "write:pets" - "read:pets" /pet/findByStatus: get: tags: - "pet" summary: "Finds Pets by status" description: "Multiple status values can be provided with comma separated strings" operationId: "findPetsByStatus" produces: - "application/xml" - "application/json" parameters: - name: "status" in: "query" description: "Status values that need to be considered for filter" required: true type: "array" items: type: "string" enum: - "available" - "pending" - "sold" default: "available" collectionFormat: "multi" responses: 200: description: "successful operation" schema: type: "array" items: $ref: "#/definitions/Pet" 400: description: "Invalid status value" security: - petstore_auth: - "write:pets" - "read:pets" securityDefinitions: petstore_auth: type: "oauth2" // one of three types; basic, apiKey and oath2 authorizationUrl: "http://petstore.swagger.io/oauth/dialog" // the URL that a user will be routed to when they're getting ready to be authorized flow: "implicit" // only applicable when you're using "OAuth2.0" as your authorization protocol scopes: write:pets: "modify pets in your account" read:pets: "read your pets" api_key: type: "apiKey" name: "api_key" in: "header" definitions: Order: type: "object" properties: id: type: "integer" format: "int64" petId: type: "integer" format: "int64" quantity: type: "integer" format: "int32" shipDate: type: "string" format: "date-time" status: type: "string" description: "Order Status" enum: - "placed" - "approved" - "delivered" complete: type: "boolean" default: false xml: name: "Order" Category: type: "object" properties: id: type: "integer" format: "int64" name: type: "string" xml: name: "Category" externalDocs: description: "Find out more about Swagger" url: "http://swagger.io"
"schemes" is referring to either "http" or "https." If you're using both, rather than separating those two values with a "," you're going to use a "-" character. your "basePath" is the path upon which all of your others are built. The "path" that's being asked for is the path you're constructing in your project. You see that in the screenshot to the right. You'll also specify whether it's a "GET" or "POST." This is also technically categorized as an "endpoint." The "tag" is an intuitive label you're using to arrange your various endpoints. The highlighted section represents a list of all your tags in your contract. You'll see each tag reiterated along with more detailed information after each path / endpoint. Operation ID is an optional unique string used to identify an operation. If provided, these IDs must be unique among all operations described in your API. An "operation" is anything that falls under the heading of something like GET, PUT, POST, etc. if your endpoint is going to "consume" some data (like in the "authentication" api we're building where it's going to be looking for a JWT in the HEAD), this is where you would document that. Otherwise, it's superflous. similar to "consume," only this time it's what your API is "producing." In the example used in the tutorial, the output is JSON. if you're getting ready to edit a resource, chances are excellent you're going to be looking at a URL that has the ID of that particular entity. This is where you would document what your app is expecting. In this example, there is not "GET," so we're set. This section would be deleted. "responses" is pretty much a default setting in this instance. This is from the website:
For each operation, you can define possible status codes, such as 200 OK or 404 Not Found, and schema of the response body. Schemas can be defined inline or referenced from an external definition via $ref. You can also provide example responses for different content types.
here's where you're going to specify your security dynamic "put" doesn't exist in this app, so you can delete all of this stuff! Remember, "put" is your operation. Next you'll have your "tag." At this point, you're still on "pet..." you're now on a new "path," hence the different indentation and look. This is going to be the route in your application and then what follows is repeat of what you've just done! here's your Security Definitions which are defined here: https://swagger.io/docs/specification/2-0/authentication/. Basically, it's the section where you're defining all of the security dynamics you've got on your app. arbitrary name you give to this particular authentication piece scopes refers to the "scope" of the authenticated user's permissions An API Key is...
An application programming interface key (API key) is a code passed in by computer programs calling an application programming interface (API) to identify the calling program, its developer, or its user to the Web site. API keys are used to track and control how the API is being used, for example to prevent malicious use or abuse of the API (as defined perhaps by terms of service).
For your purposes you will be using a JWT which will be documented as basicAuth. definitions is where you're outlining the basic schema of the relevant tables in your database. Click here to see of the types. 2) The Swagger v3 Contract (back to top...) You're working with version three of Swagger. The final working Open API contract looks like this:
openapi: 3.0.0 info: title: Healthkit Authentication description: Authenticating users coming from Healthkit to bIQ version: 1.0.0 servers: - url: <appliedsite> description: server that is being accessed by incoming Healthkit authentication request paths: /authentication: get: summary: validates incoming JWT token description: takes JWT token, breaks it up and then validates it according to what's in the database responses: '200': # status code $ref: '#/components/responses/AuthenticatedUser' '401': $ref: '#/components/responses/UnAuthenticatedUser' '403': $ref: '#/components/responses/BadToken' components: responses: AuthenticatedUser: description: user has been authenticated content: application/json: schema: type: string items: type: string UnAuthenticatedUser: description: user has not been validated content: application/json: schema: type: string items: type: string BadToken: description: token is bogus content: application/json: schema: type: string items: type: string securitySchemes: bearerAuth: # arbitrary name for the security scheme type: http scheme: bearer bearerFormat: JWT schemas: User: type: object properties: id: type: integer email: type: string password: type: string security: - bearerAuth: []
The Basic Structure and the syntax you need for the sake of Authentication is located here. You can find all kinds of real world examples by going out to https://unpkg.com/browse/openapi-directory@1.1.3/api/.
Redhat is an American multinational software company providing open-source software products to the enterprise community. Founded in 1993, Red Hat has its corporate headquarters in Raleigh, North Carolina, with other offices worldwide. It became a subsidiary of IBM on July 9, 2019
A) Apicurio (back to top...) Login by clicking on the link. Enter in your basic info and then begin with the "Design" section. Everything is pretty self-explanatory. Know that "tags" is an object that allows you to group the "paths" (endpoints) into named groups in the Swagger UI display. You don't need that for this challenge. Server Variables: You're just establishing some protocols here. Click here for more information and examples. B) Working Example (back to top...) Here's a working example of the Authentication OpenApi that I had to craft using Apicurio...
openapi: 3.0.2 info: title: Healthkit Authentication version: 1.0.0 description: Authenticating user data as it's coming from Healthkit to Applied Health Analytics contact: name: Applied Health Analytics Dev Team url: 'ontent has been removed email: content has been removed servers: - url: 'content has been removed' description: >- This is a placeholder for now. The actual URL will be better defined as we move further into development. variables: majorVersion: default: v1 description: The first crack at an epic API paths: /authentication: description: >- incoming JWT to be either authenticated, refused or with a response indicating it was not an authentic JWT servers: - url: content has been removed' description: URL of application get: responses: '200': description: user has been authenticated '400': content: application/json: schema: type: string examples: bad JWT: value: MG9LJiEK5_Db8WpF5cWWRebXCtU (contains an _ character) description: bad JWT '403': content: application/json: schema: type: string examples: bad username / password: value: username: bogus password: mcgee description: the username and password don't exist in the database security: - bearerAuth: [] summary: get JWT from user description: >- after process of authentication has been accomplished, the appropriate response is made with routing user to the next part of the app or informed that their credentials are wrong or the JWT was invalid components: schemas: username: title: Root Type for username description: username of person attempting to access bIQ from Healthkit type: object properties: username: description: username of person attempting to access bIQ from Healthkit type: string password: description: password of user attempting to access bIQ from Healthkit type: string example: username: John Doe password: description: password of user type: object properties: password: description: password of user attempting to access bIQ from Healthkit example: password: Secret responses: '200': description: OK '401': description: Not authenticated '403': description: Access token does not have the required scope securitySchemes: bearerAuth: scheme: bearer type: http description: 'JWT coming in the header as Authorization bearer: ' security: - basicAuth: []
There was an already exising sample of an authentication OpenAPI on Stoplight. Here's what that looked like:
{ "openapi": "3.0.0", "info": { "version": "1.0.0", "title": "OAuth Authentication Service API", "description": "OAuth Authentication microservice API for AHA", "contact": { "name": "AHA API Team", "email": "content has been removed", "url": "content has been removed" }, "license": { "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" } }, "servers": [ { "url": "content has been removed" } ], "paths": { "/authorization": { "get": { "description": "OAuth Authorization Endpoint", "operationId": "auth", "parameters": [ { "name": "username", "in": "query", "description": "Credential username", "required": true, "style": "form", "schema": { "type": "string" } }, { "name": "password", "in": "query", "description": "Credential password", "required": true, "style": "form", "schema": { "type": "string" } } ], "responses": { "200": { "description": "OAuth authorization response", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/AuthCredentials" } } } } }, "default": { "description": "unexpected error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "schemas": { "AuthCredentials": { "required": [ "jwt" ], "properties": { "jwt": { "type": "string" } } }, "Error": { "required": [ "code", "message" ], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } } } }
A) json_decode (back to top...) In this case, you're getting some JSON and you "decoding" it into a PHP array. It looks like this:
<?php $someJSON = '[ {"name":"Jonathan Suh","gender":"male"}, {"name":"William Philbin","gender":"male"}, {"name":"Allison McKinnery", "gender":"female"} ]'; $the_array=json_decode($someJSON, true); print_r($the_array); ?>
...and you get this: Array ( [0] => Array ( [name] => Jonathan Suh [gender] => male ) [1] => Array ( [name] => William Philbin [gender] => male ) [2] => Array ( [name] => Allison McKinnery [gender] => female ) ) If you wanted to loop through it, you would do this:
<?php $someJSON = '[ {"name":"Jonathan Suh","gender":"male"}, {"name":"William Philbin","gender":"male"}, {"name":"Allison McKinnery", "gender":"female"} ]'; $the_array=json_decode($someJSON, true); foreach ($the_array as $key => $value) { echo $value["name"] . ", " . $value["gender"] . "<br>"; } ?>
...and it would look like this: Jonathan Suh, male
William Philbin, male
Allison McKinnery, female
B) json_decode (back to top...) To convert a PHP array into a JSON string, you would do this:
<?php // Array $someArray = [ [ "name" => "Jonathan Suh", "gender" => "male" ], [ "name" => "William Philbin", "gender" => "male" ], [ "name" => "Allison McKinnery", "gender" => "female" ] ]; // Convert Array to JSON String $someJSON = json_encode($someArray); echo $someJSON; ?>
...and you get this: [{"name":"Jonathan Suh","gender":"male"},{"name":"William Philbin","gender":"male"},{"name":"Allison McKinnery","gender":"female"}] For more info, click here. You can head out to https://www.w3schools.com/js/js_json_php.asp for other examples as well. As has been mentioned before, a Boilerplate is a piece of re-useable code...
What you're looking at here is a boilerplate application for building RESTful APIs Microservice in Node.js using express and mongoose in ES6 with code coverage and JsonWebToken Authentication. A) Terms and Technologies (back to top...) Here are some terms and technologies... 1) Next Gen JavaScript - it's the same thing as ES6. ES6 is the newest verison of JavaScript and it offers some neat enhancements and shortcuts. You can read more about how functions can now be written using the "fat arrow (=>)" by clicking here as well as some other information about "let" and "const" and some other things by clicking here. 2) Babel - Babel is a compiler that "dumbs down" whatever ES6 code you've written to "old school" JavaScript so that you code can run on older servers. 3) Gulp - Gulp is a tool that you use in the context of Node that simplifes and streamlines some of your workflow.
When a user chooses to login to an app using their Facebook profile, it can be real convenient, especially if that user is already logged in to Facebook. In that instance, the user won't have to enter any login criteria and they're given access to the app they're trying to login to. Facebook assumes the device the user is utlilizing at that point is secure and the user is who they claim to be. It can be kind of shady, but that's why in some instances, some apps will ask you to field an email or answer a text to validate your identity.
B) JSON WebToken Authentication (back to top...) For the app you're building, you're not trying to access HealthKit, rather HealtKit is trying to access your database. You're not knocking on their door. Rather, they're knocking on yours. The login credentials are going to be kept in a separate database on our server. The whole purpose of their having reached out to AHA is to facilitate an ETL transaction. They're going to be coming to you with a JSON WebToken. Your job is to authenticate them based on that token and then give them clearance to proceed to the other parts of our system. So, what is a JSON WebToken? This is all coming from "bits and pieces"... A JSON Web Token (JWT) is a safe, compact, and self-contained way of transmitting information between multiple parties in the form of a JSON object. The way that it typically works is that a user wants to login to an app and clicks on the option to login using Facebook. The app knocks on the door of Facebook with the user's Facebook credentials (if the user is logged in to Facebook already) and Facebook responds with at JWT that has all of the user's info and that user is admitted into the app. The structure of the JWT is divided up into three parts: The header typically consists of two parts: the token's type, and the hashing algorithm that is being used.
{ "alg": "HS256", "typ": "JWT" }
The payload is where the actual information that we want to send is stored. Here is an example of a simple payload. Know that payloads can be way more complicated than this to ensure better security.
{ "sub": "65165751325", "name": "Rajat S", "admin": true }
The signature is used to verify that the message was not altered before reaching its destination. This is usually done by using private keys. These three parts are usually encoded into three Base64-URI strings that are separated by a . in between them. Here's what a real life JWT (pronounced "jot") looks like: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiJ1c2Vycy9Uek1Vb2NNRjRwIiwibmFtZSI6IlJvYmVydCBUb2tlbiBNYW4iLCJzY29wZSI6InNlbGYgZ3JvdXBzL2FkbWlucyIsImV4cCI6IjEzMDA4MTkzODAifQ. 1pVOLQduFWW3muii1LExVBt2TK1-MdRI4QjhKryaDwc Yes, everything is encrypted, but not always. You've basically got two different types: To easily decode, verify, generate, or just play around with JWTs, check out the JWT.IO Debugger by Auth0. A) Definition (back to top...) Dockerizing" is the facility that allows an engineer to package up an entire application and make it portable and capable of being inserted into any application / environment. Click on the link to get a run down on how to get things set up. To set up an account, click here. What makes this so crucial to our Healthkit project is that we're going to package our authentication code within the Boilerplate and then stick that into a Docker environment. A couple of key concepts: You're going to write your code to what amounts to a "Docker Image." That "image" file is run by the "Docker" facility on your server. Everything is self contained so you don't have to concern yourself with different technologies installed on your server. If you have "Docker," you're all set! A couple of changes that had to be made in order to get this to work: First of all, I needed to change the "experimental" setting to "true" in Windows 10 settings. To do that, right click on the Docker instance, go to Settings -> Daemon -> Advanced -> set "experimental" to "true." Second, I had to enable nested virtualization by going to the "Parallels" icon in the upper right hand corner of the screen and click on "Control Center." From there, click on the "gear" icon and then "CPU and Memory." You'll see the box to click on at that point.
$$.env refers to environment variables. By definition, environment variables are variables established outside a particular code block. Unlike constants or vars, these are defined outside the system. With Stoplight, the $$.env variables are going to help defined your environment which allows you to switch back and forth between two testing paradigms. Here's the actual definition: Say you have a scenario step that sends an HTTP request to authenticate a new user. The response from that request includes an apiKey that you want to use for other requests. You can easily save that apiKey to an environment variable, for later re-use, by adding a capture in the form $$.env.apiKey = output.body.apiKey. After running the step, check your current environment variables and note the newly added apiKey!
A) Stoplight (back to top...) 1) Nomenclature (back to top...) After you login to Stoplight, click on the the "Applied Health Analytics" name under "Organizations" and click on "Healthkit Integration." After that click on, "Authentication Service.oas3." Here's what you've got in place right now as far as "basics:" a) Request Parameters (back to top...) You've got two query parameters: username and password b) Responses (back to top...) The route is "authorization" 200 -> jwt string which is going to be an array. This is what happens in the event of a successful response from the server. In other words, the authorization was successful. This theoretical in that you're probable not using a GET, but this is a starting point nevertheless. default -> this is your error code. c) Send a Test Request (back to top...) i) Settings (back to top...) A couple of things: First, you'll notice on the "Settings" tab there's a snippet about "$$.env" variables. Refer to the call box to the right for info about that. CORS is something you'll need in place to protect any personal information in the context of your testing. ii) Query (back to top...) Here you're documenting your query parameters. iii) Code Generation (back to top...) This is what you get after sending a "Test Request." A lot of this, I think, are placeholders right now, as far as what's on Spotlight right now. But the thing you want to be sensitive to is the "Headers" section. "Headers" is going to be the place where you define the content type. You're not going to be rendering a page. Rather, you're going to be making available some JSON - at least I think that's the target. A) Overview (back to top...) "OAuth" means "Open Authentication..." OAuth, which is pronounced "oh-auth," allows an end user's account information to be used by third-party services, such as Facebook, without exposing the user's password. OAuth acts as an intermediary on behalf of the end user, providing the service with an access token that authorizes specific account information to be shared. The process for obtaining the token is called a flow. The bottom line is that you're not exposing the user's password. When you login into APIcurio using your Github account, you're not using your username and password from Github to login. Rather, a pre-manufactured agreement has been established by both Github and APIcurio which includes a specific URL. Click here to watch a video that details that process. Imagine "MyBucksApplication" provides great dashboard views of financial information. Sarah wants to view her "Memorial Bank" statements via the "MyBucksApplication." She logs into "MyBucks" and "MyBucks" reaches out to "Memorial Bank." "Memorial Bank" receives an Authorization Request, answers it in the affirmative and then "MyBucks" responds with an Authorization Grant, "Memorial" gives "MyBucks" an Access Token. That Token gives "MyBucks" access to specified areas of "Memorial Bank" that are particular to Sarah. Notice there's no exchange of Sarah's login credentials that are particular to "Memorial Bank." That's the beauty and the utility of OAuth. Click here to watch a video that gives you an Overview of OAuth. A) Working OpenAPI Contract (back to top...) 1) Swagger Version (back to top...) When you go out to Swagger's Editor, you're given the "Petstore" example which represents a sound starting point right out of the chute and it's here where you can begin writing your contract. Here's the Authentication code that passed the Swagger error check:
openapi: 3.0.2 info: title: Healthkit Authentication version: 1.0.0 description: >- Authenticating user data as it's coming from Healthkit to Applied Health Analytics contact: name: Applied Health Analytics Dev Team url: 'companyURL email: companyURL servers: - url: 'companyURL' description: >- This is a placeholder for now. The actual URL will be better defined as we move further into development. variables: majorVersion: default: v1 description: The first crack at an epic API paths: /authentication: description: >- incoming JWT to be either authenticated, refused or with a response indicating it was not an authentic JWT servers: - url: 'companyURL' description: URL of application get: responses: '200': description: user has been authenticated '400': content: application/json: schema: type: string examples: bad JWT: value: JWTValue (contains an _ character) description: bad JWT '403': content: application/json: schema: type: string examples: bad username / password: value: username: bogus password: mcgee description: the username and password don't exist in the database security: - bearerAuth: [] summary: get JWT from user description: >- after process of authentication has been accomplished, the appropriate response is made with routing user to the next part of the app or informed that their credentials are wrong or the JWT was invalid components: schemas: username: title: Root Type for username description: username of person attempting to access bIQ from Healthkit type: object properties: username: description: username of person attempting to access bIQ from Healthkit type: string password: description: password of user attempting to access bIQ from Healthkit type: string example: username: John Doe password: description: password of user type: object properties: password: description: password of user attempting to access bIQ from Healthkit example: password: Secret responses: '200': description: OK '401': description: Not authenticated '403': description: Access token does not have the required scope securitySchemes: bearerAuth: scheme: bearer type: http description: 'JWT coming in the header as Authorization bearer: ' security: - basicAuth: []
After you've finished Editing, you can save it to your "SwaggerHub" account. 2) Stoplight JSON Version (back to top...) When you import your code from Swagger to Spotlight, you want to be sensitive to the fact that Stoplight expects JSON code and it's a little more "picky" when it comes to certain elements of your code. In this case, it was the "tags" portion of the "tags" element which is lighlighted below:
{ "openapi": "3.0.0", "info": { "title": "Healthkit Authentication", "description": "Authenticating users coming from Healthkit to bIQ", "contact": { "name": "AHA API Team", "email": "companyURL", "url": "<appliedsite>" }, "version": "1.0.0" }, "servers": [ { "url": "<appliedsite>", "description": "server that is being accessed by incoming Healthkit authentication request" } ], "paths": { "/authentication": { "get": { "tags": [ "authenticate" ], "summary": "validates incoming JWT token", "description": "takes JWT token, breaks it up and then validates it according to what's in the database", "operationId": "auth", "responses": { "200": { "$ref": "#/components/responses/AuthenticatedUser" }, "401": { "$ref": "#/components/responses/UnAuthenticatedUser" }, "403": { "$ref": "#/components/responses/BadToken" } } } } }, "components": { "responses": { "AuthenticatedUser": { "description": "user has been authenticated", "content": { "application/json": { "schema": { "type": "string", "items": { "type": "string" } } } } }, "UnAuthenticatedUser": { "description": "user has not been validated", "content": { "application/json": { "schema": { "type": "string", "items": { "type": "string" } } } } }, "BadToken": { "description": "token is bogus", "content": { "application/json": { "schema": { "type": "string", "items": { "type": "string" } } } } } }, "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } }, "schemas": { "User": { "type": "object", "properties": { "id": { "type": "integer" }, "email": { "type": "string" }, "password": { "type": "string" } } } } }, "security": [ { "bearerAuth": [] } ] }
B) JWT / Node Tutorial (back to top...) This is a based on a video tutorial that runs through JWT authorization on Node:
const express = require('express'); const jwt = require('jsonwebtoken'); const app = express(); app.get('/api', (req, res) => { res.json({ message: 'Welcome to the API' }); }); app.post('/api/posts', verifyToken, (req, res) => { jwt.verify(req.token, 'secretkey', (err, authData) => { if(err) { res.sendStatus(403); } else { res.json({ message: 'Post created...', authData }); } }); }); app.post('/api/login', (req, res) => { // Mock user const user = { id: 1, username: 'brad', email: 'brad@gmail.com' } jwt.sign({user}, 'secretkey', { expiresIn: '30s' }, (err, token) => { res.json({ token }); }); }); // FORMAT OF TOKEN // Authorization: Bearer <access_token> // Verify Token function verifyToken(req, res, next) { // Get auth header value const bearerHeader = req.headers['authorization']; // Check if bearer is undefined if(typeof bearerHeader !== 'undefined') { // Split at the space const bearer = bearerHeader.split(' '); // Get token from array const bearerToken = bearer[1]; // Set the token req.token = bearerToken; // Next middleware next(); } else { // Forbidden res.sendStatus(403); } } app.listen(5000, () => console.log('Server started on port 5000'));
import express "jsonwebtoken" is used to return the JsonWebToken as a string. initialize the "app" variable with express just a basic route / index page this is the route we want to protect. The "protective" dynamic is where you see verifyToken this is how we're getting our JSONWebToken usually it's here where we would retrieve our user from the database. For this example, we're creating a user on the fly. Again, this is what would normally be coming from the database or, for the sake of this instance, this is what's going to be coming to us - a fully validated user that will have all of the info within it's payload.
One term you'll encouter when reading through explanations about JWTs is "SPA" or "Single Page Application." Basically it's that webpage that never reloads because it's being presented by a JavaScript application that's running the background. Click here for more info.
here's where you're creating your token and your payload etc. This goes back to the documentation that you see at https://www.npmjs.com/package/jsonwebtoken. There it's documented as both synconous and asyncronous. This is synronous so we're using a callback. This particular line is utilizing ES6. Again, this is producing the token. this is where you're breaking your token down into its payload etc. This is middleware in that you're accessing the request and the response objects as well as what represents the "next" middleware function in the application's request-response cycle. your JWT is coming to you in the header. You're going to find it documented as Bearer <access_token>. That is what's going to come back provided it's not undefined. checking to make sure bearer exists using "split" which turns a string into an array. With this little device you can isolate the value associated with the "Authorization" value. Remember, that's going to be // Authorization: Bearer <access_token>. You're "splitting" it at the " " between "Bearer" and the actual token. in your array, the [0] value is "Bearer" and the [1] value is your token. So, const bearerToken is going to be your token. setting the request object token to the bearer token. keep things moving using "next" this is your callback that includes the "req.token" that was established with your "verifyToken" function. "secretkey" can be any value. Usually it's a highly random value that is kept confidential and only accessible to your server. your "authData" is going to look like this: { "authData": { "user": { "id": 1, "username", "brad", "email": "brad@gmail.com" }, "iat": 1513884407 // iat stands for "expires in" }, } That's the content that was encryped as your JWT on jwt.sign({user}. The way this going to translate to your project is that instead of the "login portion" () that's generating your token, instead you'll have that token embedded in your header. // The way your OpenAPI is going to expect that as something that is stored in local storage in your header. The header title will be "Authorization" with a value of "bearer" and then the token. C) Actual Authentication (back to top...) Using the above as a template, here's the final working code:
require("dotenv").config(); const express = require("express"); const jwt = require("jsonwebtoken"); const app = express(); const mongoose = require("mongoose"); const session = require("express-session"); const MongoDBStore = require("connect-mongodb-session")(session); const csrf = require("csurf"); const superPassword = process.env.SIGNATURE; //console.log(superPassword); const MONGODB_URI = "databaseURL"; app.get("/api", (req, res) => { res.json({ message: "Welcome to the API" }); }); app.post("/api/posts", verifyToken, (req, res) => { jwt.verify(req.token, superPassword, (err, authData) => { if (err) { res.sendStatus(403); } else { res.json({ message: "Post created", authData }); console.log(authData.user.username); } }); }); app.post("/api/login", (req, res) => { //Mock User const user = { id: 1, username: "brad", email: "brad@yahoo.com" }; // jwt.sign({ user: user }, "secretkey", { expiresIn: "30s" }, (err, token) => { jwt.sign({ user: user }, superPassword, (err, token) => { res.json({ token: token }); }); }); // format of Token // Authorization: Bearer <access_token> //verify Token function verifyToken(req, res, next) { //get auth header value const bearerHeader = req.headers["authorization"]; // check if bearer is undefined if (typeof bearerHeader !== "undefined") { // split at the space const bearer = bearerHeader.split(" "); //get token from array const bearerToken = bearer[1]; console.log(bearerToken); //set the token req.token = bearerToken; next(); } else { //forbidden res.sendStatus(403); } } mongoose .connect(MONGODB_URI) .then(result => { app.listen(5000); }) .catch(err => { console.log(err); });
The above works great! Be aware of the highlighted sections which represent the implementation of the ".env" dynamic where you're definining your signature as a server variable. That ".env" file is in your root directory. 1) user.js (back to top...)
This section is in case a database needed to be included in order to facilitate the "mining" of the payload as criteria to be used in a SELECT
The first thing you want to is set up your "user.js" model which will house your username, SSN, DOB etc. You're going to do that by setting up a new database in MongoDB Compass and then initiating a new collection through your "user.js" model in your app. That's going to look like this: const mongoose = require("mongoose"); const Schema = mongoose.Schema; const userSchema = new Schema({ username: { type: String, required: true }, ssn: { type: String, required: true }, dob: { type: String, required: true }, email: { type: String, required: true }, password: { type: String, required: true } }); Notice how you're establishing teh "Schema" constant right out of the chute. As you might expect, this is a package you're having to import on your "app.js" page. That, along with several other packages will need to in place. Let's see what that's going to look like... 2) app.js (back to top...) We're going to break this down into stages... First of all, here are you packages: const express = require("express"); const jwt = require("jsonwebtoken"); const app = express(); const mongoose = require("mongoose"); const session = require("express-session"); const MongoDBStore = require("connect-mongodb-session")(session); const csrf = require("csurf"); const MONGODB_URI = "databaseURL"; The big thing, in addition to all of the crucial things that we've already covered, is the database connection. You'll see referenced at the very end of "app.js" like this: ... mongoose .connect(MONGODB_URI) .then(result => { app.listen(5000); }) .catch(err => { console.log(err); }); 3) Setting Up a User (back to top...) First of all, run your app so the model that you set up with "user.js" establishes the correct columns and fields etc. Next, you can manually insert your user with all of the necessary data with the exception of the encrypted password. To encrypt your password, rather than create a form that captures what's in the database and then use "bcyrptjs" tool, you can click here to do it online. Great way to get it done for the sake of drill. 4) Finding the User (back to top...) The way this is going to work is you're going to use "Postman" to post a user as a collection of Bearer Tokens that will be an encrypted version of the user's DOB and the last four of their SSN. That will be compared to what's in the database and if there's a match. D) Deployment (back to top...) Good PUtty tutorial: https://www.youtube.com/watch?v=_GSOnHRYSS0 E) Authentication w/ Database (back to top...) Final version where we're actually grabbing the corresponding token / info from a database and comparing that value to the incoming JWT rather than hard wiring the token and leaving that potentially exposed.
require("dotenv").config(); const express = require("express"); const jwt = require("jsonwebtoken"); const mongoose = require("mongoose"); const app = express(); const superPassword = process.env.SIGNATURE; const conn = process.env.CONNECTION; const tokenID = process.env.THE_TOKEN_ID; const Authorization = require("./models/authorization"); app.post("/authentication", verifyToken, (req, res) => { jwt.verify(req.token, superPassword, (err, authData) => { if (err) { res.sendStatus(403); } else { res.json({ message: "user is authenticated", authData }); } }); }); function verifyToken(req, res, next) { const bearerHeader = req.headers["authorization"]; if (typeof bearerHeader !== "undefined") { const bearer = bearerHeader.split(" "); const bearerToken = bearer[1]; Authorization.findById(tokenID).then(security => { if (bearerToken == security.token) { req.token = security.token; next(); } else { res.sendStatus(403); } }); } } mongoose .connect(conn) .then(result => { app.listen(5000); }) .catch(err => { console.log(err); });
this is the functionality that allows you to create and utilize CONSTANTS. In this instance, you're using: SIGNATURE="secretkey" CONNECTION = "mongodb connection" THE_TOKEN = "your token" THE_TOKEN_ID="token id" This is your ".env" file and it's referenced again a few lines later. this is the functionality you need to decrypt an incoming JWT the next three lines are constants that you've stored in your .env file in the root directory here, you're importing your authorization.js file which looks like this: const mongoose = require("mongoose"); const Schema = mongoose.Schema; const authorizationSchema = new Schema({ app: { type: String, required: true }, token: { type: String, required: true } }); module.exports = mongoose.model("Authorization", authorizationSchema);
As an aside, that collection isn't automatically created until you actually use it! In other words, you've got to create a document within that collection in order for it to show up in the database!
while this has been covered before, it bears repeating. This whole process of "verifyToken" is to simply define the "req.token" value according to what's coming in via the Header, or at least it was in the previous tutorial. In the tutorial, we were simply ensuring that there was a value and, if so, we "split" it so we could read the token apart from the "Bearer" dynamic. In this instance, we're doing that but we're also comparing that to the token associated with the "_id" of the token that corresponds to the value we have stored as "THE_TOKEN_ID" in our ".env" file. If the incoming JWT is valid and it matches the token that's associated with the "_id" we have stored in our ".env" file, then "req.token"