text: Add Auth to Your PWA with Okta and Stencil
  Progressive Web Applications (PWAs) are the newest technology on the web dev block and they’ve arrived just in time to solve a growing problem. Many companies are struggling to keep isolated development teams across their organization up-to-date when new features are released. Some companies are even trying to decide if it’s worth the cost to develop both a web app and a mobile application. Not surprisingly, it’s a headache most companies are looking to avoid. PWAs can give companies a lot of the features they need from a mobile app without the need to manage multiple teams and codebases.
  A PWA performs spectacularly when on a fast connection and still performs well when offline or on Wi-Fi that’s not really fast enough to be called Wi-Fi (sometimes called “Lie-Fi”). It does so via caching and a JavaScript “Service Worker” that intercepts server calls and tries to serve the data from cache first, then when the server finally responds, it will replace the cached data with possibly “fresher” data from the server.
  Recently, the Ionic team released a new project called Stencil. Stencil is a compiler that generates standards-compliant web components. Unlike most JavaScript frameworks, it doesn’t deliver a “framework” of code to the browser. It simply takes the code you write and uses its compiler to create vanilla components. You can also use Stencil’s compiler with your favorite framework. The Stencil starter project is the easiest way to get started with Stencil and produces a base application that scores nearly a 100% score on Lighthouse’s progressive web app scorecard.
  To get started building a PWA using Stencil, clone the starter application and detach it from the GitHub remote.
  WARNING: Stencil is not at a 1.0 release yet (as of this writing). So be aware that if you continue, you’re in early-adopter territory. If you find bugs, submit an issue.
  Set Up the Starter Application
  git clone first-stencil cd first-stencil git remote rm origin
  Then, install all the dependencies that your new Stencil app will need.
  npm install
  You may see a couple of warnings from
  node-pre-gyp around
  fsevents. There’s nothing to see here. This is just to get around a nasty little npm bug.
  Next, add the Okta Auth SDK (via CDN) to the bottom of the
  index.html page, right before the closing
  </body> tag.
  <script src="" type="text/javascript"></script>
  While there is an npm package for Okta’s Auth SDK, Stencil has a hard time compiling it. Overall it works better for now if you just use the CDN to include it.
  If you’re like me, the next thing you’ll want to do is run
  npm start and check the site with Lighthouse. If you do, you’ll notice that the score is kind of low. In particular, it doesn’t register a service worker or return a 200 when offline, That’s because it’s a development build, and generally, you don’t want the service worker intercepting server calls and returning cached data in development.
  To ensure an accurate depiction of the kind of PWA you get out of the box with Stencil, make sure to run a production build using
  npm run build. Once you do, you’ll see a
  www folder and inside that folder, you’ll see a
  sw.js file. That’s your service worker!
  Set Up Your Okta Application
  If you haven’t already done so, create a free-forever developer account at.
  Once you’ve registered, click on Applications in the top menu. Then click Add Application.
  You will then be taken to the application creation wizard. Choose Single-Page App and click Next at the bottom.
  On the next screen, you’ll see the default settings provided by the single-page application template. Change the name of the application to something more descriptive, like “Stencil SPA”. Also, change the base URIs and the login redirect URIs settings to use port 3333 because that’s where your application will be running. The rest of the default settings are fine.
  Click Done at the bottom.
  Select your newly created application from the listing, and click on the General tab to view the general settings.
  At the bottom, you’ll see a Client ID setting (yours won’t be blurred out, obviously). Copy this to use in your Stencil application. You will also need your Okta organization URL, which you can find at the top right of the Dashboard page.
  Add the Authentication Component
  In the
  components folder, add a new folder called
  app-auth. This is where your login page component will go. You can call it whatever you want, I’m just following the naming conventions set out by the starter app here. I’d definitely recommend deciding on a naming convention early and sticking with it.
  Inside the newly created
  app-auth folder create two files:
  app-auth.css and
  app-auth.tsx. Start by creating the shell of the
  app-auth.tsx file.
  import { Component } from '@stencil/core'; @Component({ tag: 'app-auth', styleUrl: 'app-auth.css' }) export class AppAuth { render() { return <div>Hello</div>; } }
  If you are like I was, you might be thinking, “What kind of Frankenstein framework is this?”
  You’ll notice the
  @Component decorator over the
  AppAuth class declaration like Angular, and then a
  render() method at the bottom like React. To me, that’s the beauty of Stencil. It takes some of the best parts of both popular frameworks and uses them to compile reusable components!
  Add a Login Form
  Next, you’ll add the JSX (that’s right, I said it) to the new component. Change the
  render() method to:
  render() { return ( <form class="app-auth"> <div class="form-item"> <label> Username: <input type="text" name="username" autocomplete="username" /> </label> </div> <div class="form-item"> <label> Password: <input type="password" name="password" autocomplete="current-password" /> </label> </div> <div class="form-actions"> <button type="button" onClick={() => this.login()}> Login </button> </div> </form> ); }
  This is just a regular JSX form, but the login button’s
  onClick event is currently wired to function that doesn’t exist.
  Add the Login Method’s Dependencies
  Before you create that function, you’ll need to set up the
  OktaAuth JavaScript object to call the Okta API for authentication. You’ll add the object to the component’s state, so right below the class declaration, add the following line:
  @State() authClient: any;
  You will also have to import the
  @State() decorator. This is is used for values related to the internal state of the component. In the first
  import statement add State to the deconstruction list.
  import { Component, State } from '@stencil/core'
  You’ll also need to get the username and password values from the form itself, so add an
  @Element() to the code right below that
  @State() like you just created, so it read like this:
  @State() authClient: any; @Element() host: HTMLElement;
  Then also add the
  @Element() decorator to the import so it reads:
  import { Component, State, Element } from '@stencil/core';
  One last thing that the
  login() function will need is access to the router, so you can redirect the user to their profile page if their authentication is successful. You’ll need a class property, so add it right below the
  @Element.
  @State() authClient: any; @Element() host: HTMLElement; @Prop() history: RouterHistory;
  To import it, add the
  @Prop() decorator to the main import and then import the
  RouterHistory from
  @stencil/router right below the core import statement. The
  @Prop decorator is used to define properties that can be passed in to your component. In this case, it’s not a passed in value, but it could be if need be. Your final import section should read:
  import { Component, Prop, State, Element, Listen } from '@stencil/core'; import { RouterHistory } from '@stencil/router';
  Finally, to use the
  OktaAuth JavaScript library you brought in from the CDN, add a declaration for it right below the import statements.
  declare const OktaAuth: any;
  Add the Login Method
  Now you included everything you’ll need to get the login function to authenticate users with your Okta organization. First, set up the
  OktaAuth object in the constructor of the
  AppAuth class. Right below the property for the
  RouterHistory object, add:
  constructor() { this.authClient = new OktaAuth({ clientId: '{clientId}', url: 'https://{yourOktaDomain}', issuer: 'default' }); }
  You can get your Client ID from that general settings page of your Okta application.
  You’ll also need your Okta org URL, from the upper-right side of the Okta dashboard page.
  Now everything is set up for the
  login() function, so you’ll create that next. Right above the
  render() method, add a
  login() function.
  login() { let inputs = this.host.querySelectorAll('input'); let user = { username: inputs[0].value, password: inputs[1].value }; return this.authClient.signIn(user) .then(res => { if (res.status === 'SUCCESS') { return this.authClient.token .getWithoutPrompt({ responseType: 'id_token', scopes: ['openid', 'profile', 'email'], sessionToken: res.sessionToken, redirectUri: '' }) .then(token => { localStorage.setItem( 'okta_id_token', JSON.stringify(token) ); this.history.push('/profile', {}); }); } else { throw `Unable to handle ${res.status} status code`; } }) .fail(function(err) { console.error(err); }); }
  Since is really the “meat” of the component, I’ll walk you through what’s going on here.
  The first thing, is getting all the inputs inside the
  form element of the component. Then a user object is created with the username and password from their respective inputs.
  Next the
  authClient object is used to call the
  signIn() method with the user object that was created. It returns a promise, so you handle the
  then condition by getting the response and check to see if the response’s status is a 200. If it is, call the
  authClient.token’s
  getWithoutPrompt() method which also returns a promise. It takes a
  responseType property which is set to
  id_token, because that’s what you want to get from Okta. You’ve also asked for three scopes that will give you the openid, profile, and email data associated with the newly authenticated user. The method need the session token returned from the
  signIn() method’s response. Finally, you’ve told the function to call back to the
  redirectUri, which was set as a trusted redirect origin in Okta when you created your application.
  In the
  then condition of this promise, you take the id token received and set it in local storage as
  okta_id_token. If all that worked, the user is redirected to the profile page.
  If the response had a status of anything other than 200, it merely throws an error that says it can’t handle any other statuses. Finally, the fail condition for the
  Simplify Login
  While this works, there are two things that would make this component a little nicer: being able to hit enter to log in instead of having to click the login button, and not even showing the login form if the person tries to go the login page when they’re already logged in.
  To achieve this, add a method to take the user directly to the profile page if they’re already logged in. Like React components, Stencil components have lifecycle methods. Instead of
  componentWillMount() for React, Stencil has a
  componentWillLoad() method, so that’s what you’ll use here.
  componentWillLoad() { let idToken = localStorage.getItem('okta_id_token'); if (idToken) { this.history.push('/profile', {}); } }
  Simply put, all you’re doing is reading the token from local storage. If one exists, you’re assuming they are logged in and redirecting them to the profile page.
  The last thing that will make this login form easier to use is to add the ability to submit the form with the enter key. Stencil has some built-in listeners for key presses. In this case, use the ‘keydown.enter’ listener. Import the
  @Listen() decorator in the very top import statement where you imported
  Component.
  import { Component, Prop, State, Element, Listen } from '@stencil/core';
  Then add a handler for the ‘keydown.enter’ event just below the
  componentWillLoad() function.
  @Listen('keydown.enter') handleEnter() { this.login(); }
  Update the Profile Page
  Now that you have a nice login page, update the profile page so that it shows the user’s claims once they’re logged in.
  First, you’ll need a type to put the user’s claims in. So create a new file in the
  app-profile folder called
  AppUser.tsx. The contents are simple, but long. I simply looked at all the claims in the token stored in
  localStorage and created an interface that matched it. So the
  AppUser.tsx file is as follows:
  interface AppUser { sub: string; name: string; locale: string; email: string; ver: number; iss: string; aud: string; iat: number; exp: number; jti: string; amr: string[]; idp: string; nonce: string; nickname: string; preferred_username: string; given_name: string; family_name: string; zoneinfo: string; updated_at: number; email_verified: boolean; auth_time: number; }
  Once you have a type to declare for your profile’s user object, update the
  app-profile.tsx file.
  The imports at the top should look like:
  import { Component, Prop, State } from '@stencil/core'; import { RouterHistory } from '@stencil/router';
  Remove the
  @Prop() line for
  match and replace is with:
  @Prop() history: RouterHistory; @State() user: AppUser; @Prop({ context: 'isServer' }) private isServer: boolean;
  The
  isServer property is a special property. Because Stencil supports prerendering and
  localStorage may not be available during prerender, you’ll need to wrap the
  localStorage calls in an
  if(!isServer){} to make sure it will build for production. This shouldn’t stop it from working, it’s just a work around for the build process.
  For the
  componentWillLoad() method, just read in the user information from the ‘okta_id_token’ in local storage:
  componentWillLoad() { if (!this.isServer) { let token = JSON.parse(localStorage.getItem('okta_id_token')); if (token) { this.user = token.claims; } else { this.history.push('/login', {}); } } }
  This will be your protector for the profile page as well, it just checks if the token exists. If so, it loads the claims from it. If not, it redirects to the login page.
  For the
  render() method, change it to display the claims in a list.
  render() { if (this.user) { let keys = Object.keys(this.user); return <div class="app-profile"> <h2>User Claims</h2> <ul> {keys.map(key => <li><span>{key}</span>: {this.user[key]}</li>)} </ul> <button onClick={this.logout}> Logout </button> </div>; } }
  The only thing left is to add the
  logout() method. This will just remove the token from local storage and reload the page, this will force the
  componentWillLoad() to redirect the user to the login page.
  logout() { if (!this.isServer) { localStorage.removeItem('okta_id_token'); location.reload(); } }
  Set Up the Login Route
  The only thing left is to add the route to the login component to the application so that users can get there.
  In the
  components/my-app/my-app.tsx file add the route inside the
  stencil-router component so that the final section looks like this:
  <stencil-router> <stencil-route <stencil-route </stencil-router>
  You’ll also need to update the route for the link on the home page. In
  components/app-home/app-home.tsx update the
  stencil-route-link element’s url to no longer pass in the url parameter.
  <stencil-route-link <button> Profile page </button> </stencil-route-link>
  That’s it! You should now be able to run the app, click on the profile page, get redirected to the login page, and be redirected back to the profile page once you’ve logged in. The profile page should show all your claims after you’ve authenticated.
  Congratulations, you now have a PWA with authentication in it, ready to go conquer the world!
  Add Styles
  As extra credit, you might want to add some styling to the login form and the profile page. Below is my style sheet for the login page that goes in
  app-auth.css:
  .app-auth { width: 30%; margin: 2rem auto; } .app-auth .form-item { padding: .25rem; } .app-auth label { width: 100%; font-size: 1rem; color: #999; } .app-auth label input { width: 97%; border-radius: .25rem; font-size: 1.5rem; } .app-auth .form-actions { text-align: right; }
  Finally, in
  app-profile.css just some simple styles to bold the label of each item.
  .app-profile { padding: 10px; } .app-profile ul li span { font-weight: bold; }
  Now when you run the app, you will see a nicely styled application, ready for the web!
  Learn More
  To learn more about PWAs, check out Matt Raible’s ultimate guide to PWAs on the Okta developer blog.
  If you want to know more about Ionic apps, check out Matt’s blog post about building Ionic apps.
  You can learn about building a basic CRUD app with VueJS from Brandon Parise’s post.
  Finally, right here you can see how to set up user registration with Node and React!
  As always, if you have any questions you can comment below or hit me up on Twitter @leebrandt and don’t forget to follow @OktaDev for great content from our communities + all the news about Okta’s developer platform!