OAuth (React)

The OAuth2 component provides a simple way to authenticate users so they can gain access to your cloud data sources.

When users sign-in using OAuth2, your application gets an accessToken that identifies the user, which may provide them with permissions to read and write to the data source.

This example uses React.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Firestore, OAuth2 } from '@grapecity/wijmo.cloud'; import { Tooltip, PopupPosition, SortDescription } from '@grapecity/wijmo'; import { DataMap } from '@grapecity/wijmo.grid'; import { FlexGrid, FlexGridColumn } from '@grapecity/wijmo.react.grid'; const API_KEY = 'AIzaSyCvuXEzP57I5CQ9ifZDG2_K8M3nDa1LOPE'; class App extends React.Component { constructor(props) { super(props); // create the Firestore data source const PROJECT_ID = 'test-9c0be'; this._fs = new Firestore(PROJECT_ID, API_KEY, { collections: ['Products', 'Categories', 'Suppliers'] }); // expose products, current user let products = this._fs.getCollection('Products'); products.sortDescriptions.push(new SortDescription('ProductID', true)); let mapCat = new DataMap(this._fs.getCollection('Categories'), 'CategoryID', 'CategoryName'); let mapSup = new DataMap(this._fs.getCollection('Suppliers'), 'SupplierID', 'CompanyName'); this.state = { products: products, mapCat: mapCat, mapSup: mapSup, user: null }; } componentDidMount() { // create the OAuth2 component const CLIENT_ID = '60621001861-h0u4ek4kmd3va9o2bubhq9ean0bgrhu2.apps.googleusercontent.com'; const SCOPES = ['https://www.googleapis.com/auth/userinfo.email']; const auth = new OAuth2(API_KEY, CLIENT_ID, SCOPES, { error: (s, e) => { console.log(JSON.stringify(e.error, null, 2)); } }); // button to log in/out let oAuthBtn = document.getElementById('auth-btn'), oAuthTip = new Tooltip({ cssClass: 'auth-tip', position: PopupPosition.BelowRight, gap: 0 }); // click button to log user in or out oAuthBtn.addEventListener('click', () => { if (auth.user) { auth.signOut(); } else { auth.signIn(); } }); // update button/sheet state when user changes auth.userChanged.addHandler((s) => { let user = s.user; oAuthBtn.textContent = user ? 'Sign Out' : 'Sign In'; oAuthTip.setTooltip(oAuthBtn, user ? `<b>Signed in as</b><br/> ${user.firstName}<br/> <img src="${user.imageUrl}"/><br/> <span class="e-mail">${user.eMail}</span>` : null); // update Firestore id token this._fs.idToken = user ? s.idToken : null; // Firestore authentication //this._fs.accessToken = user ? s.accessToken : null; // IAM authentication // allow editing if we are authenticated this.setState({ user: user }); // update message document.getElementById('auth-msg').textContent = user ? 'You are signed in, so you may edit the grid (if you have permissions).' : 'You are not signed in, so you cannot edit the grid.'; }); } render() { return <div className='container-fluid'> <div className="auth"> <div id="auth-msg"></div> <button id="auth-btn" className="btn btn-primary"> Sign In </button> </div> <FlexGrid selectionMode="MultiRange" showMarquee={true} allowAddNew={true} allowDelete={true} isReadOnly={!this.state.user} itemsSource={this.state.products} autoGenerateColumns={false}> <FlexGridColumn binding="ProductID" header="ID"/> <FlexGridColumn binding="ProductName" header="Product Name" width={200}/> <FlexGridColumn binding="CategoryID" header="Category" width={150} dataMap={this.state.mapCat}/> <FlexGridColumn binding="SupplierID" header="Supplier" width={150} dataMap={this.state.mapSup}/> <FlexGridColumn binding="UnitPrice" header="Unit Price" format="n2"/> <FlexGridColumn binding="QuantityPerUnit" header="Qty per Unit" width={150}/> <FlexGridColumn binding="UnitsInStock" header="Units in Stock"/> <FlexGridColumn binding="Discontinued"/> </FlexGrid> </div>; } } ReactDOM.render(<App />, document.getElementById('app'));
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo OAuth2</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html>
.wj-flexgrid { height: 350px; } .auth { display: flex; align-items: center; justify-content: space-between; padding: 12px; } .auth-tip { text-align: right; background: #fffacf8e; border-radius: 0; } .auth-tip .e-mail { font-size: 70%; font-style: italic; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.grid.immutable': 'npm:@grapecity/wijmo.grid.immutable/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', "@grapecity/wijmo.react.chart.analytics": "npm:@grapecity/wijmo.react.chart.analytics/index.js", "@grapecity/wijmo.react.chart.animation": "npm:@grapecity/wijmo.react.chart.animation/index.js", "@grapecity/wijmo.react.chart.annotation": "npm:@grapecity/wijmo.react.chart.annotation/index.js", "@grapecity/wijmo.react.chart.finance.analytics": "npm:@grapecity/wijmo.react.chart.finance.analytics/index.js", "@grapecity/wijmo.react.chart.finance": "npm:@grapecity/wijmo.react.chart.finance/index.js", "@grapecity/wijmo.react.chart.hierarchical": "npm:@grapecity/wijmo.react.chart.hierarchical/index.js", "@grapecity/wijmo.react.chart.interaction": "npm:@grapecity/wijmo.react.chart.interaction/index.js", "@grapecity/wijmo.react.chart.radar": "npm:@grapecity/wijmo.react.chart.radar/index.js", "@grapecity/wijmo.react.chart": "npm:@grapecity/wijmo.react.chart/index.js", "@grapecity/wijmo.react.core": "npm:@grapecity/wijmo.react.core/index.js", "@grapecity/wijmo.react.gauge": "npm:@grapecity/wijmo.react.gauge/index.js", "@grapecity/wijmo.react.grid.detail": "npm:@grapecity/wijmo.react.grid.detail/index.js", "@grapecity/wijmo.react.grid.filter": "npm:@grapecity/wijmo.react.grid.filter/index.js", "@grapecity/wijmo.react.grid.grouppanel": "npm:@grapecity/wijmo.react.grid.grouppanel/index.js", '@grapecity/wijmo.react.grid.search': 'npm:@grapecity/wijmo.react.grid.search/index.js', "@grapecity/wijmo.react.grid.multirow": "npm:@grapecity/wijmo.react.grid.multirow/index.js", "@grapecity/wijmo.react.grid.sheet": "npm:@grapecity/wijmo.react.grid.sheet/index.js", '@grapecity/wijmo.react.grid.transposed': 'npm:@grapecity/wijmo.react.grid.transposed/index.js', '@grapecity/wijmo.react.grid.immutable': 'npm:@grapecity/wijmo.react.grid.immutable/index.js', "@grapecity/wijmo.react.grid": "npm:@grapecity/wijmo.react.grid/index.js", "@grapecity/wijmo.react.input": "npm:@grapecity/wijmo.react.input/index.js", "@grapecity/wijmo.react.olap": "npm:@grapecity/wijmo.react.olap/index.js", "@grapecity/wijmo.react.viewer": "npm:@grapecity/wijmo.react.viewer/index.js", "@grapecity/wijmo.react.nav": "npm:@grapecity/wijmo.react.nav/index.js", "@grapecity/wijmo.react.base": "npm:@grapecity/wijmo.react.base/index.js", 'jszip': 'npm:jszip/dist/jszip.js', 'react': 'npm:react/umd/react.production.min.js', 'react-dom': 'npm:react-dom/umd/react-dom.production.min.js', 'redux': 'npm:redux/dist/redux.min.js', 'react-redux': 'npm:react-redux/dist/react-redux.min.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);