├── .bowerrc ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app.yaml ├── bower.json ├── package.json ├── requirements.txt ├── step04 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html ├── step05 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html ├── step06 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html ├── step07 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html ├── step08 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html ├── step09 ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static │ ├── bower_components │ ├── elements │ │ ├── elements.html │ │ └── elements.vulcanized.html │ ├── favicon.png │ ├── images │ │ ├── default_img.png │ │ ├── facebook_signin.png │ │ ├── google_signin.png │ │ ├── homescreen144.png │ │ ├── homescreen192.png │ │ ├── homescreen48.png │ │ ├── homescreen72.png │ │ ├── homescreen96.png │ │ └── howto │ │ │ ├── add-second-account.png │ │ │ ├── auto-sign-in-with-2-accounts.png │ │ │ ├── auto-sign-in.png │ │ │ ├── fb_config.png │ │ │ ├── gsi_config.png │ │ │ ├── opt-in-to-smart-lock.png │ │ │ ├── save-password.png │ │ │ ├── sign-up.png │ │ │ └── the-first-sign-in.png │ ├── manifest.json │ └── scripts │ │ ├── app.js │ │ ├── auto.js │ │ ├── federation.js │ │ ├── index.js │ │ ├── main.js │ │ └── signin.js └── templates │ ├── index.html │ ├── layout.html │ ├── main.html │ └── signin.html └── working ├── app.yaml ├── client_secrets.json ├── lib ├── main.py ├── static ├── bower_components ├── elements │ ├── elements.html │ └── elements.vulcanized.html ├── favicon.png ├── images │ ├── default_img.png │ ├── facebook_signin.png │ ├── google_signin.png │ ├── homescreen144.png │ ├── homescreen192.png │ ├── homescreen48.png │ ├── homescreen72.png │ ├── homescreen96.png │ └── howto │ │ ├── add-second-account.png │ │ ├── auto-sign-in-with-2-accounts.png │ │ ├── auto-sign-in.png │ │ ├── fb_config.png │ │ ├── gsi_config.png │ │ ├── opt-in-to-smart-lock.png │ │ ├── save-password.png │ │ ├── sign-up.png │ │ └── the-first-sign-in.png ├── manifest.json └── scripts │ ├── app.js │ ├── auto.js │ ├── federation.js │ ├── index.js │ ├── main.js │ └── signin.js └── templates ├── index.html ├── layout.html ├── main.html └── signin.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components/" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | lib 3 | node_modules 4 | bower_components 5 | client_secrets.json 6 | *.pyc 7 | package-lock.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/bcrypt"] 2 | path = lib/bcrypt 3 | url = https://github.com/erlichmen/py-bcrypt 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles. 6 | 7 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 8 | * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 9 | * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 10 | 11 | Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to 12 | accept your pull requests. 13 | 14 | ## Contributing A Patch 15 | 16 | 1. Submit an issue describing your proposed change to the repo in question. 17 | 1. The repo owner will respond to your issue promptly. 18 | 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 19 | 1. Fork the desired repo, develop and test your code changes. 20 | 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. 21 | 1. Submit a pull request. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Credential Management API Codelab Material 2 | 3 | The codelab is live at [g.co/codelabs/cmapi](https://g.co/codelabs/cmapi) 4 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: credential-management-api-codelab 2 | version: 1 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: true 6 | 7 | libraries: 8 | - name: ssl 9 | version: latest 10 | - name: pycrypto 11 | version: latest 12 | 13 | handlers: 14 | - url: .* 15 | script: main.app 16 | secure: always 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "version": "1.0.0", 4 | "authors": [ 5 | "Eiji Kitamura " 6 | ], 7 | "description": "A codelab material for experiencing the Credential Management API", 8 | "license": "Apache-2.0", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests" 15 | ], 16 | "dependencies": { 17 | "fetch": "~0.10.0", 18 | "url-search-params": "^0.5.0", 19 | "polymer": "^1.4.0", 20 | "promise-polyfill": "https://github.com/PolymerLabs/promise-polyfill.git#^1.0.0", 21 | "web-animations-js": "^2.2.1", 22 | "paper-elements": "PolymerElements/paper-elements#^1.0.7", 23 | "iron-elements": "PolymerElements/iron-elements#^1.0.10" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "credential-management-api-codelab", 3 | "version": "1.0.0", 4 | "description": "A codelab material for experiencing the Credential Management API", 5 | "main": "index.js", 6 | "scripts": { 7 | "preinstall": "git submodule init; git submodule update;", 8 | "postinstall": "pip install -t lib -r requirements.txt; node_modules/bower/bin/bower install;", 9 | "start": "dev_appserver.py working --host=0.0.0.0 --port=8080 --admin_port=8081" 10 | }, 11 | "author": "Eiji Kitamura ", 12 | "license": "Apache-2.0", 13 | "devDependencies": { 14 | "bower": "^1.7.9" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | oauth2client 3 | google-api-python-client 4 | pyopenssl 5 | pycrypto 6 | -------------------------------------------------------------------------------- /step04/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step04/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step04/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step04/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # coding: -*- utf-8 -*- 16 | 17 | from google.appengine.ext import vendor 18 | vendor.add('lib') 19 | 20 | import os 21 | import sys 22 | import binascii 23 | import json 24 | import urllib 25 | from bcrypt import bcrypt 26 | from flask import Flask, request, make_response, render_template,\ 27 | session, redirect, url_for 28 | from oauth2client import client 29 | 30 | from google.appengine.ext import ndb 31 | from google.appengine.api import urlfetch 32 | 33 | # Does `client_secrets.json` file exist? 34 | if os.path.isfile('client_secrets.json') is False: 35 | sys.exit('client_secrets.json not found.') 36 | 37 | # Load `client_secrets.json` file 38 | keys = json.loads(open('client_secrets.json', 'r').read())['web'] 39 | 40 | CLIENT_ID = keys['client_id'] 41 | 42 | app = Flask( 43 | __name__, 44 | static_url_path='', 45 | static_folder='static', 46 | template_folder='templates' 47 | ) 48 | app.debug = True 49 | 50 | # `SECRET_KEY` can be anything as long as it is hidden, but we use 51 | # `client_secret` here for convenience 52 | SECRET_KEY = keys['client_secret'] 53 | app.config.update( 54 | SECRET_KEY=SECRET_KEY 55 | ) 56 | 57 | 58 | # App Engine Datastore to save credentials 59 | class CredentialStore(ndb.Model): 60 | profile = ndb.JsonProperty() 61 | 62 | @classmethod 63 | def remove(cls, key): 64 | ndb.Key(cls.__name__, key).delete() 65 | 66 | @classmethod 67 | def hash(cls, password): 68 | return bcrypt.hashpw(password, bcrypt.gensalt()) 69 | 70 | @classmethod 71 | def verify(cls, password, hashed): 72 | if bcrypt.hashpw(password, hashed) == hashed: 73 | return True 74 | else: 75 | return False 76 | 77 | 78 | @app.before_request 79 | def csrf_protect(): 80 | # All incoming POST requests will pass through this 81 | if request.method == 'POST': 82 | # Obtain CSRF token embedded in the session 83 | csrf_token = session.get('csrf_token', None) 84 | # Compare the POST'ed CSRF token with the one in the session 85 | if not csrf_token or csrf_token != request.form.get('csrf_token'): 86 | # Return 403 if empty or they are different 87 | return make_response('Forbidden', 403) 88 | 89 | 90 | @app.route('/') 91 | def index(): 92 | # Issue a CSRF token if not included in the session 93 | if 'csrf_token' not in session: 94 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 95 | 96 | # Obtain id from session 97 | id = session.get('id', None) 98 | 99 | # If session includes `id`, the user is already signed in 100 | if id is not None: 101 | store = CredentialStore.get_by_id(id) 102 | if store is not None: 103 | return redirect(url_for('main')) 104 | 105 | return render_template('index.html', 106 | path=request.path, 107 | client_id=CLIENT_ID, 108 | csrf_token=session['csrf_token']) 109 | 110 | 111 | @app.route('/main') 112 | def main(): 113 | # TODO: Is this required? 114 | if 'csrf_token' not in session: 115 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 116 | 117 | # Obtain id from session 118 | id = session.get('id', None) 119 | 120 | # If session doesn't include `id`, the user is not signed in 121 | if id is None: 122 | return redirect(url_for('signin')) 123 | 124 | # Obtain Datastore entry by email address 125 | store = CredentialStore.get_by_id(id) 126 | 127 | # If the store doesn't exist, fail. 128 | if store is None: 129 | return redirect(url_for('signin')) 130 | 131 | profile = store.profile 132 | 133 | return render_template('main.html', 134 | path=request.path, 135 | name=profile['name'], 136 | imageUrl=profile['imageUrl'], 137 | csrf_token=session['csrf_token']) 138 | 139 | 140 | @app.route('/signin') 141 | def signin(): 142 | if 'csrf_token' not in session: 143 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 144 | 145 | return render_template('signin.html', 146 | path=request.path, 147 | client_id=CLIENT_ID, 148 | csrf_token=session['csrf_token']) 149 | 150 | 151 | @app.route('/auth/password', methods=['POST']) 152 | def pwauth(): 153 | # The POST should include `email` 154 | email = request.form.get('email', None)[:32] 155 | # The POST should include `password` 156 | password = request.form.get('password', None)[:32] 157 | 158 | if not email or not password: 159 | return make_response('Authentication failed', 401) 160 | 161 | # Obtain Datastore entry by email address 162 | store = CredentialStore.get_by_id(email) 163 | 164 | # If the store doesn't exist, fail. 165 | if store is None: 166 | return make_response('Authentication failed', 401) 167 | 168 | profile = store.profile 169 | 170 | # If the profile doesn't exist, fail. 171 | if profile is None: 172 | return make_response('Authentication failed', 401) 173 | 174 | # If the password doesn't match, fail. 175 | if CredentialStore.verify(password, profile['password']) is False: 176 | return make_response('Authentication failed', 401) 177 | 178 | session['id'] = email 179 | 180 | # Not making a session for demo purpose/simplicity 181 | return make_response('Authenticated', 200) 182 | 183 | 184 | @app.route('/auth/google', methods=['POST']) 185 | def gauth(): 186 | # The POST should include `id_token` 187 | id_token = request.form.get('id_token', '')[:3072] 188 | 189 | # Verify the `id_token` using API Client Library 190 | idinfo = client.verify_id_token(id_token, CLIENT_ID) 191 | 192 | # Additional verification: See if `iss` matches Google issuer string 193 | if idinfo['iss'] not in ['accounts.google.com', 194 | 'https://accounts.google.com']: 195 | return make_response('Authentication failed', 401) 196 | 197 | id = idinfo['sub'] 198 | 199 | # For now, we'll always store profile data after successfully 200 | # verifying the token and consider the user authenticated. 201 | store = CredentialStore.get_by_id(id) 202 | 203 | if store is None: 204 | store = CredentialStore(id=id) 205 | 206 | # Construct a profile object 207 | store.profile = { 208 | 'id': id, 209 | 'imageUrl': idinfo.get('picture', None), 210 | 'name': idinfo.get('name', None), 211 | 'email': idinfo.get('email', None) 212 | } 213 | store.put() 214 | 215 | session['id'] = id 216 | 217 | # Not making a session for demo purpose/simplicity 218 | return make_response('Authenticated', 200) 219 | 220 | 221 | @app.route('/register', methods=['POST']) 222 | def register(): 223 | # The POST should include `email` 224 | email = request.form.get('email', None)[:32] 225 | 226 | # The POST should include `password` 227 | _password = request.form.get('password', None)[:32] 228 | 229 | # Validate the parameters POST'ed (intentionally not too strict) 230 | if not email or not _password: 231 | return make_response('Bad Request', 400) 232 | 233 | # Hash password 234 | password = CredentialStore.hash(_password) 235 | # Perform relevant sanitization/validation on your own code. 236 | # This demo omits them on purpose for simplicity. 237 | profile = { 238 | 'id': email, 239 | 'email': email, 240 | 'name': request.form.get('name', ''), 241 | 'password': password, 242 | 'imageUrl': '/images/default_img.png' 243 | } 244 | 245 | # Overwrite existing user 246 | store = CredentialStore(id=profile['id'], profile=profile) 247 | store.put() 248 | 249 | session['id'] = profile['id'] 250 | 251 | # Not making a session for demo purpose/simplicity 252 | return make_response('Registered', 200) 253 | 254 | 255 | @app.route('/unregister', methods=['POST']) 256 | def unregister(): 257 | # Obtain id from session 258 | id = session.get('id', None) 259 | 260 | # If session includes `id`, the user is already signed in 261 | if id is None: 262 | return make_response('Authentication failed', 401) 263 | store = CredentialStore.get_by_id(id) 264 | if store is None: 265 | return make_response('Authentication failed', 401) 266 | 267 | profile = store.profile 268 | 269 | if profile is None: 270 | return make_response('Authentication failed', 401) 271 | 272 | # Remove the user account 273 | CredentialStore.remove(id) 274 | 275 | # Not terminating a session for demo purpose/simplicity 276 | return make_response('Unregistered', 200) 277 | 278 | 279 | @app.route('/signout') 280 | def signout(): 281 | # Terminate sessions 282 | session.pop('id', None) 283 | 284 | # Not terminating a session for demo purpose/simplicity 285 | return redirect(url_for('index', 286 | quote='You are signed out')) 287 | -------------------------------------------------------------------------------- /step04/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step04/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step04/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/favicon.png -------------------------------------------------------------------------------- /step04/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/default_img.png -------------------------------------------------------------------------------- /step04/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step04/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/google_signin.png -------------------------------------------------------------------------------- /step04/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/homescreen144.png -------------------------------------------------------------------------------- /step04/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/homescreen192.png -------------------------------------------------------------------------------- /step04/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/homescreen48.png -------------------------------------------------------------------------------- /step04/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/homescreen72.png -------------------------------------------------------------------------------- /step04/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/homescreen96.png -------------------------------------------------------------------------------- /step04/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step04/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step04/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step04/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step04/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step04/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step04/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step04/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step04/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step04/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step04/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step04/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 71 | return Promise.resolve(); 72 | } else { 73 | return Promise.reject(); 74 | } 75 | }); 76 | }).then(function() { 77 | location.href = '/main?quote=You are signed in with Google SignIn'; 78 | }, function() { 79 | app.fire('show-toast', { 80 | text: 'Google Sign-In failed' 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /step04/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | // TODO 6-1: Define `autoSignIn()` function 2 | // TODO 7-1: Sign-In a user upon landing the page -------------------------------------------------------------------------------- /step04/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /step04/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | // TODO 6-5: Sign-In a user by pressing a "Sign-In" button 35 | location.href = '/signin'; 36 | }); 37 | -------------------------------------------------------------------------------- /step04/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | // TODO 8-1: Turn off auto sign-in when a user signs out 4 | location.href = '/signout'; 5 | }); 6 | 7 | var unregForm = document.querySelector('#unregForm'); 8 | unregForm.addEventListener('submit', function(e) { 9 | e.preventDefault(); 10 | 11 | fetch('/unregister', { 12 | method: 'POST', 13 | credentials: 'include', 14 | body: new FormData(unregForm) 15 | }).then(function(res) { 16 | if (res.status === 200) { 17 | // TODO 8-2: Turn off auto sign-in when a user unregisters 18 | location.href = '/?quote=You are unregistered'; 19 | } else { 20 | app.fire('show-toast', { 21 | text: 'Unregister failed' 22 | }); 23 | } 24 | }, function() { 25 | app.fire('show-toast', { 26 | text: 'Unregister failed' 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /step04/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | location.href = '/main?quote=You are signed in'; 12 | } else { 13 | app.fire('show-toast', { 14 | text: 'Authentication failed' 15 | }); 16 | } 17 | }, function() { 18 | app.fire('show-toast', { 19 | text: 'Authentication failed' 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /step04/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step04/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step04/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step04/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 18 | 27 |
28 | 31 |
32 |
33 |
34 | or 35 |
36 |
37 | 38 |
39 |
40 |
{% endblock %} 41 | -------------------------------------------------------------------------------- /step05/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step05/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step05/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step05/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # coding: -*- utf-8 -*- 16 | 17 | from google.appengine.ext import vendor 18 | vendor.add('lib') 19 | 20 | import os 21 | import sys 22 | import binascii 23 | import json 24 | import urllib 25 | from bcrypt import bcrypt 26 | from flask import Flask, request, make_response, render_template,\ 27 | session, redirect, url_for 28 | from oauth2client import client 29 | 30 | from google.appengine.ext import ndb 31 | from google.appengine.api import urlfetch 32 | 33 | # Does `client_secrets.json` file exist? 34 | if os.path.isfile('client_secrets.json') is False: 35 | sys.exit('client_secrets.json not found.') 36 | 37 | # Load `client_secrets.json` file 38 | keys = json.loads(open('client_secrets.json', 'r').read())['web'] 39 | 40 | CLIENT_ID = keys['client_id'] 41 | 42 | app = Flask( 43 | __name__, 44 | static_url_path='', 45 | static_folder='static', 46 | template_folder='templates' 47 | ) 48 | app.debug = True 49 | 50 | # `SECRET_KEY` can be anything as long as it is hidden, but we use 51 | # `client_secret` here for convenience 52 | SECRET_KEY = keys['client_secret'] 53 | app.config.update( 54 | SECRET_KEY=SECRET_KEY 55 | ) 56 | 57 | 58 | # App Engine Datastore to save credentials 59 | class CredentialStore(ndb.Model): 60 | profile = ndb.JsonProperty() 61 | 62 | @classmethod 63 | def remove(cls, key): 64 | ndb.Key(cls.__name__, key).delete() 65 | 66 | @classmethod 67 | def hash(cls, password): 68 | return bcrypt.hashpw(password, bcrypt.gensalt()) 69 | 70 | @classmethod 71 | def verify(cls, password, hashed): 72 | if bcrypt.hashpw(password, hashed) == hashed: 73 | return True 74 | else: 75 | return False 76 | 77 | 78 | @app.before_request 79 | def csrf_protect(): 80 | # All incoming POST requests will pass through this 81 | if request.method == 'POST': 82 | # Obtain CSRF token embedded in the session 83 | csrf_token = session.get('csrf_token', None) 84 | # Compare the POST'ed CSRF token with the one in the session 85 | if not csrf_token or csrf_token != request.form.get('csrf_token'): 86 | # Return 403 if empty or they are different 87 | return make_response('Forbidden', 403) 88 | 89 | 90 | @app.route('/') 91 | def index(): 92 | # Issue a CSRF token if not included in the session 93 | if 'csrf_token' not in session: 94 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 95 | 96 | # Obtain id from session 97 | id = session.get('id', None) 98 | 99 | # If session includes `id`, the user is already signed in 100 | if id is not None: 101 | store = CredentialStore.get_by_id(id) 102 | if store is not None: 103 | return redirect(url_for('main')) 104 | 105 | return render_template('index.html', 106 | path=request.path, 107 | client_id=CLIENT_ID, 108 | csrf_token=session['csrf_token']) 109 | 110 | 111 | @app.route('/main') 112 | def main(): 113 | # TODO: Is this required? 114 | if 'csrf_token' not in session: 115 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 116 | 117 | # Obtain id from session 118 | id = session.get('id', None) 119 | 120 | # If session doesn't include `id`, the user is not signed in 121 | if id is None: 122 | return redirect(url_for('signin')) 123 | 124 | # Obtain Datastore entry by email address 125 | store = CredentialStore.get_by_id(id) 126 | 127 | # If the store doesn't exist, fail. 128 | if store is None: 129 | return redirect(url_for('signin')) 130 | 131 | profile = store.profile 132 | 133 | return render_template('main.html', 134 | path=request.path, 135 | name=profile['name'], 136 | imageUrl=profile['imageUrl'], 137 | csrf_token=session['csrf_token']) 138 | 139 | 140 | @app.route('/signin') 141 | def signin(): 142 | if 'csrf_token' not in session: 143 | session['csrf_token'] = binascii.hexlify(os.urandom(24)) 144 | 145 | return render_template('signin.html', 146 | path=request.path, 147 | client_id=CLIENT_ID, 148 | csrf_token=session['csrf_token']) 149 | 150 | 151 | @app.route('/auth/password', methods=['POST']) 152 | def pwauth(): 153 | # The POST should include `email` 154 | email = request.form.get('email', None)[:32] 155 | # The POST should include `password` 156 | password = request.form.get('password', None)[:32] 157 | 158 | if not email or not password: 159 | return make_response('Authentication failed', 401) 160 | 161 | # Obtain Datastore entry by email address 162 | store = CredentialStore.get_by_id(email) 163 | 164 | # If the store doesn't exist, fail. 165 | if store is None: 166 | return make_response('Authentication failed', 401) 167 | 168 | profile = store.profile 169 | 170 | # If the profile doesn't exist, fail. 171 | if profile is None: 172 | return make_response('Authentication failed', 401) 173 | 174 | # If the password doesn't match, fail. 175 | if CredentialStore.verify(password, profile['password']) is False: 176 | return make_response('Authentication failed', 401) 177 | 178 | session['id'] = email 179 | 180 | # Not making a session for demo purpose/simplicity 181 | return make_response('Authenticated', 200) 182 | 183 | 184 | @app.route('/auth/google', methods=['POST']) 185 | def gauth(): 186 | # The POST should include `id_token` 187 | id_token = request.form.get('id_token', '')[:3072] 188 | 189 | # Verify the `id_token` using API Client Library 190 | idinfo = client.verify_id_token(id_token, CLIENT_ID) 191 | 192 | # Additional verification: See if `iss` matches Google issuer string 193 | if idinfo['iss'] not in ['accounts.google.com', 194 | 'https://accounts.google.com']: 195 | return make_response('Authentication failed', 401) 196 | 197 | id = idinfo['sub'] 198 | 199 | # For now, we'll always store profile data after successfully 200 | # verifying the token and consider the user authenticated. 201 | store = CredentialStore.get_by_id(id) 202 | 203 | if store is None: 204 | store = CredentialStore(id=id) 205 | 206 | # Construct a profile object 207 | store.profile = { 208 | 'id': id, 209 | 'imageUrl': idinfo.get('picture', None), 210 | 'name': idinfo.get('name', None), 211 | 'email': idinfo.get('email', None) 212 | } 213 | store.put() 214 | 215 | session['id'] = id 216 | 217 | # Not making a session for demo purpose/simplicity 218 | return make_response('Authenticated', 200) 219 | 220 | 221 | @app.route('/register', methods=['POST']) 222 | def register(): 223 | # The POST should include `email` 224 | email = request.form.get('email', None)[:32] 225 | 226 | # The POST should include `password` 227 | _password = request.form.get('password', None)[:32] 228 | 229 | # Validate the parameters POST'ed (intentionally not too strict) 230 | if not email or not _password: 231 | return make_response('Bad Request', 400) 232 | 233 | # Hash password 234 | password = CredentialStore.hash(_password) 235 | # Perform relevant sanitization/validation on your own code. 236 | # This demo omits them on purpose for simplicity. 237 | profile = { 238 | 'id': email, 239 | 'email': email, 240 | 'name': request.form.get('name', ''), 241 | 'password': password, 242 | 'imageUrl': '/images/default_img.png' 243 | } 244 | 245 | # Overwrite existing user 246 | store = CredentialStore(id=profile['id'], profile=profile) 247 | store.put() 248 | 249 | session['id'] = profile['id'] 250 | 251 | # Not making a session for demo purpose/simplicity 252 | return make_response('Registered', 200) 253 | 254 | 255 | @app.route('/unregister', methods=['POST']) 256 | def unregister(): 257 | # Obtain id from session 258 | id = session.get('id', None) 259 | 260 | # If session includes `id`, the user is already signed in 261 | if id is None: 262 | return make_response('Authentication failed', 401) 263 | store = CredentialStore.get_by_id(id) 264 | if store is None: 265 | return make_response('Authentication failed', 401) 266 | 267 | profile = store.profile 268 | 269 | if profile is None: 270 | return make_response('Authentication failed', 401) 271 | 272 | # Remove the user account 273 | CredentialStore.remove(id) 274 | 275 | # Not terminating a session for demo purpose/simplicity 276 | return make_response('Unregistered', 200) 277 | 278 | 279 | @app.route('/signout') 280 | def signout(): 281 | # Terminate sessions 282 | session.pop('id', None) 283 | 284 | # Not terminating a session for demo purpose/simplicity 285 | return redirect(url_for('index', 286 | quote='You are signed out')) 287 | -------------------------------------------------------------------------------- /step05/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step05/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step05/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/favicon.png -------------------------------------------------------------------------------- /step05/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/default_img.png -------------------------------------------------------------------------------- /step05/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step05/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/google_signin.png -------------------------------------------------------------------------------- /step05/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/homescreen144.png -------------------------------------------------------------------------------- /step05/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/homescreen192.png -------------------------------------------------------------------------------- /step05/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/homescreen48.png -------------------------------------------------------------------------------- /step05/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/homescreen72.png -------------------------------------------------------------------------------- /step05/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/homescreen96.png -------------------------------------------------------------------------------- /step05/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step05/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step05/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step05/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step05/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step05/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step05/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step05/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step05/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step05/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step05/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step05/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | 21 | /* 22 | Although this sample app is using Polymer, most of the interactions are 23 | handled using regular APIs so you don't have to learn about it. 24 | */ 25 | var app = document.querySelector('#app'); 26 | // Set an event listener to show a toast. (Polymer) 27 | app.listeners = { 28 | 'show-toast': 'showToast' 29 | }; 30 | 31 | /** 32 | * Polymer event handler to show a toast. 33 | * @param {Event} e Polymer custom event object 34 | * @return {void} 35 | */ 36 | app.showToast = function(e) { 37 | this.$.toast.text = e.detail.text; 38 | this.$.toast.show(); 39 | }; 40 | 41 | app.addEventListener('dom-change', function() { 42 | var url = new URL(location.href); 43 | var params = new URLSearchParams(url.search.slice(1)); 44 | if (params.get('quote')) { 45 | app.fire('show-toast', { 46 | text: params.get('quote') 47 | }); 48 | } 49 | }); 50 | 51 | /** 52 | * When google sign-in button is pressed. 53 | * @return {void} 54 | */ 55 | var gsignin = document.querySelector('#gsignin'); 56 | gsignin.addEventListener('click', function() { 57 | gSignIn() 58 | .then(function(googleUser) { 59 | // Now user is successfully authenticated with Google. 60 | // Send ID Token to the server to authenticate with our server. 61 | var form = new FormData(); 62 | form.append('id_token', googleUser.getAuthResponse().id_token); 63 | form.append('csrf_token', document.querySelector('#csrf_token').value); 64 | 65 | return fetch('/auth/google', { 66 | method: 'POST', 67 | credentials: 'include', 68 | body: form 69 | }).then(function(res) { 70 | if (res.status === 200) { 71 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 72 | return Promise.resolve(); 73 | } else { 74 | return Promise.reject(); 75 | } 76 | }); 77 | }).then(function() { 78 | location.href = '/main?quote=You are signed in with Google SignIn'; 79 | }, function() { 80 | app.fire('show-toast', { 81 | text: 'Google Sign-In failed' 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /step05/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | // TODO 6-1: Define `autoSignIn()` function 2 | // TODO 7-1: Sign-In a user upon landing the page -------------------------------------------------------------------------------- /step05/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /step05/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | // TODO 6-5: Sign-In a user by pressing a "Sign-In" button 35 | location.href = '/signin'; 36 | }); 37 | -------------------------------------------------------------------------------- /step05/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | // TODO 8-1: Turn off auto sign-in when a user signs out 4 | location.href = '/signout'; 5 | }); 6 | 7 | var unregForm = document.querySelector('#unregForm'); 8 | unregForm.addEventListener('submit', function(e) { 9 | e.preventDefault(); 10 | 11 | fetch('/unregister', { 12 | method: 'POST', 13 | credentials: 'include', 14 | body: new FormData(unregForm) 15 | }).then(function(res) { 16 | if (res.status === 200) { 17 | // TODO 8-2: Turn off auto sign-in when a user unregisters 18 | location.href = '/?quote=You are unregistered'; 19 | } else { 20 | app.fire('show-toast', { 21 | text: 'Unregister failed' 22 | }); 23 | } 24 | }, function() { 25 | app.fire('show-toast', { 26 | text: 'Unregister failed' 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /step05/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(form); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are signed in'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are signed in'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Authentication failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Authentication failed' 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /step05/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step05/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step05/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step05/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 29 |
30 | 33 |
34 |
35 |
36 | or 37 |
38 |
39 | 40 |
41 |
42 |
{% endblock %} 43 | -------------------------------------------------------------------------------- /step06/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step06/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step06/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step06/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step06/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step06/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/favicon.png -------------------------------------------------------------------------------- /step06/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/default_img.png -------------------------------------------------------------------------------- /step06/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step06/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/google_signin.png -------------------------------------------------------------------------------- /step06/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/homescreen144.png -------------------------------------------------------------------------------- /step06/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/homescreen192.png -------------------------------------------------------------------------------- /step06/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/homescreen48.png -------------------------------------------------------------------------------- /step06/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/homescreen72.png -------------------------------------------------------------------------------- /step06/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/homescreen96.png -------------------------------------------------------------------------------- /step06/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step06/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step06/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step06/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step06/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step06/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step06/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step06/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step06/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step06/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step06/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step06/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 71 | return Promise.resolve(); 72 | } else { 73 | return Promise.reject(); 74 | } 75 | }); 76 | }).then(function() { 77 | location.href = '/main?quote=You are signed in with Google SignIn'; 78 | }, function() { 79 | app.fire('show-toast', { 80 | text: 'Google Sign-In failed' 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /step06/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | var autoSignIn = function(mode) { 2 | if (cmapiAvailable) { 3 | return navigator.credentials.get({ 4 | // TODO 8-4: Reflect a silent access 5 | password: true 6 | }).then(function(cred) { 7 | if (cred) { 8 | var form = new FormData(); 9 | var csrf_token = document.querySelector('#csrf_token').value; 10 | form.append('csrf_token', csrf_token); 11 | 12 | switch (cred.type) { 13 | case 'password': 14 | form.append('email', cred.id); 15 | form.append('password', cred.password); 16 | return fetch('/auth/password', { 17 | method: 'POST', 18 | credentials: 'include', 19 | body: form 20 | }); 21 | } 22 | return Promise.reject(); 23 | } else { 24 | return Promise.reject(); 25 | } 26 | }).then(function(res) { 27 | if (res.status === 200) { 28 | return Promise.resolve(); 29 | } else { 30 | return Promise.reject(); 31 | } 32 | }); 33 | } else { 34 | return Promise.reject(); 35 | } 36 | }; 37 | // TODO 7-1: Sign-In a user upon landing the page 38 | -------------------------------------------------------------------------------- /step06/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /step06/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | autoSignIn() 35 | .then(function() { 36 | location.href = '/main?quote=You are signed in'; 37 | }, function() { 38 | location.href = '/signin'; 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /step06/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | // TODO 8-1: Turn off auto sign-in when a user signs out 4 | location.href = '/signout'; 5 | }); 6 | 7 | var unregForm = document.querySelector('#unregForm'); 8 | unregForm.addEventListener('submit', function(e) { 9 | e.preventDefault(); 10 | 11 | fetch('/unregister', { 12 | method: 'POST', 13 | credentials: 'include', 14 | body: new FormData(unregForm) 15 | }).then(function(res) { 16 | if (res.status === 200) { 17 | // TODO 8-2: Turn off auto sign-in when a user unregisters 18 | location.href = '/?quote=You are unregistered'; 19 | } else { 20 | app.fire('show-toast', { 21 | text: 'Unregister failed' 22 | }); 23 | } 24 | }, function() { 25 | app.fire('show-toast', { 26 | text: 'Unregister failed' 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /step06/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(form); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are signed in'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are signed in'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Authentication failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Authentication failed' 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /step06/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step06/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step06/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step06/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 29 |
30 | 33 |
34 |
35 |
36 | or 37 |
38 |
39 | 40 |
41 |
42 |
{% endblock %} 43 | -------------------------------------------------------------------------------- /step07/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step07/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step07/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step07/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step07/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step07/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/favicon.png -------------------------------------------------------------------------------- /step07/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/default_img.png -------------------------------------------------------------------------------- /step07/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step07/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/google_signin.png -------------------------------------------------------------------------------- /step07/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/homescreen144.png -------------------------------------------------------------------------------- /step07/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/homescreen192.png -------------------------------------------------------------------------------- /step07/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/homescreen48.png -------------------------------------------------------------------------------- /step07/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/homescreen72.png -------------------------------------------------------------------------------- /step07/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/homescreen96.png -------------------------------------------------------------------------------- /step07/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step07/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step07/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step07/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step07/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step07/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step07/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step07/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step07/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step07/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step07/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step07/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 71 | return Promise.resolve(); 72 | } else { 73 | return Promise.reject(); 74 | } 75 | }); 76 | }).then(function() { 77 | location.href = '/main?quote=You are signed in with Google SignIn'; 78 | }, function() { 79 | app.fire('show-toast', { 80 | text: 'Google Sign-In failed' 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /step07/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | var autoSignIn = function(mode) { 2 | if (cmapiAvailable) { 3 | return navigator.credentials.get({ 4 | // TODO 8-4: Reflect a silent access 5 | password: true 6 | }).then(function(cred) { 7 | if (cred) { 8 | var form = new FormData(); 9 | var csrf_token = document.querySelector('#csrf_token').value; 10 | form.append('csrf_token', csrf_token); 11 | 12 | switch (cred.type) { 13 | case 'password': 14 | form.append('email', cred.id); 15 | form.append('password', cred.password); 16 | return fetch('/auth/password', { 17 | method: 'POST', 18 | credentials: 'include', 19 | body: form 20 | }); 21 | } 22 | return Promise.reject(); 23 | } else { 24 | return Promise.reject(); 25 | } 26 | }).then(function(res) { 27 | if (res.status === 200) { 28 | return Promise.resolve(); 29 | } else { 30 | return Promise.reject(); 31 | } 32 | }); 33 | } else { 34 | return Promise.reject(); 35 | } 36 | }; 37 | 38 | // TODO 8-3: Reflect a silent access 39 | autoSignIn().then(function() { 40 | location.href = '/main?quote=You are automatically signed in'; 41 | }, function() { 42 | console.log('auto sign-in skipped'); 43 | }); 44 | -------------------------------------------------------------------------------- /step07/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /step07/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | autoSignIn() 35 | .then(function() { 36 | location.href = '/main?quote=You are signed in'; 37 | }, function() { 38 | location.href = '/signin'; 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /step07/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | // TODO 8-1: Turn off auto sign-in when a user signs out 4 | location.href = '/signout'; 5 | }); 6 | 7 | var unregForm = document.querySelector('#unregForm'); 8 | unregForm.addEventListener('submit', function(e) { 9 | e.preventDefault(); 10 | 11 | fetch('/unregister', { 12 | method: 'POST', 13 | credentials: 'include', 14 | body: new FormData(unregForm) 15 | }).then(function(res) { 16 | if (res.status === 200) { 17 | // TODO 8-2: Turn off auto sign-in when a user unregisters 18 | location.href = '/?quote=You are unregistered'; 19 | } else { 20 | app.fire('show-toast', { 21 | text: 'Unregister failed' 22 | }); 23 | } 24 | }, function() { 25 | app.fire('show-toast', { 26 | text: 'Unregister failed' 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /step07/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(form); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are signed in'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are signed in'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Authentication failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Authentication failed' 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /step07/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step07/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step07/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step07/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 29 |
30 | 33 |
34 |
35 |
36 | or 37 |
38 |
39 | 40 |
41 |
42 |
{% endblock %} 43 | -------------------------------------------------------------------------------- /step08/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step08/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step08/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step08/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step08/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step08/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/favicon.png -------------------------------------------------------------------------------- /step08/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/default_img.png -------------------------------------------------------------------------------- /step08/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step08/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/google_signin.png -------------------------------------------------------------------------------- /step08/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/homescreen144.png -------------------------------------------------------------------------------- /step08/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/homescreen192.png -------------------------------------------------------------------------------- /step08/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/homescreen48.png -------------------------------------------------------------------------------- /step08/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/homescreen72.png -------------------------------------------------------------------------------- /step08/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/homescreen96.png -------------------------------------------------------------------------------- /step08/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step08/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step08/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step08/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step08/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step08/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step08/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step08/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step08/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step08/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step08/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step08/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 71 | return Promise.resolve(); 72 | } else { 73 | return Promise.reject(); 74 | } 75 | }); 76 | }).then(function() { 77 | location.href = '/main?quote=You are signed in with Google SignIn'; 78 | }, function() { 79 | app.fire('show-toast', { 80 | text: 'Google Sign-In failed' 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /step08/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | var autoSignIn = function(mode) { 2 | if (cmapiAvailable) { 3 | return navigator.credentials.get({ 4 | password: true, 5 | mediation: mode 6 | }).then(function(cred) { 7 | if (cred) { 8 | var form = new FormData(); 9 | var csrf_token = document.querySelector('#csrf_token').value; 10 | form.append('csrf_token', csrf_token); 11 | 12 | switch (cred.type) { 13 | case 'password': 14 | form.append('email', cred.id); 15 | form.append('password', cred.password); 16 | return fetch('/auth/password', { 17 | method: 'POST', 18 | credentials: 'include', 19 | body: form 20 | }); 21 | } 22 | return Promise.reject(); 23 | } else { 24 | return Promise.reject(); 25 | } 26 | }).then(function(res) { 27 | if (res.status === 200) { 28 | return Promise.resolve(); 29 | } else { 30 | return Promise.reject(); 31 | } 32 | }); 33 | } else { 34 | return Promise.reject(); 35 | } 36 | }; 37 | 38 | autoSignIn('silent').then(function() { 39 | location.href = '/main?quote=You are automatically signed in'; 40 | }, function() { 41 | console.log('auto sign-in skipped'); 42 | }); 43 | -------------------------------------------------------------------------------- /step08/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /step08/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | autoSignIn() 35 | .then(function() { 36 | location.href = '/main?quote=You are signed in'; 37 | }, function() { 38 | location.href = '/signin'; 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /step08/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | if (cmapiAvailable) { 4 | navigator.credentials.preventSilentAccess() 5 | .then(function() { 6 | location.href = '/signout'; 7 | }); 8 | } else { 9 | location.href = '/signout'; 10 | } 11 | }); 12 | 13 | var unregForm = document.querySelector('#unregForm'); 14 | unregForm.addEventListener('submit', function(e) { 15 | e.preventDefault(); 16 | 17 | fetch('/unregister', { 18 | method: 'POST', 19 | credentials: 'include', 20 | body: new FormData(unregForm) 21 | }).then(function(res) { 22 | if (res.status === 200) { 23 | if (cmapiAvailable) { 24 | navigator.credentials.preventSilentAccess() 25 | .then(function() { 26 | location.href = '/?quote=You are unregistered'; 27 | }); 28 | } else { 29 | location.href = '/?quote=You are unregistered'; 30 | } 31 | } else { 32 | app.fire('show-toast', { 33 | text: 'Unregister failed' 34 | }); 35 | } 36 | }, function() { 37 | app.fire('show-toast', { 38 | text: 'Unregister failed' 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /step08/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(form); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are signed in'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are signed in'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Authentication failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Authentication failed' 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /step08/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step08/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step08/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step08/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 29 |
30 | 33 |
34 |
35 |
36 | or 37 |
38 |
39 | 40 |
41 |
42 |
{% endblock %} 43 | -------------------------------------------------------------------------------- /step09/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /step09/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /step09/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /step09/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /step09/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /step09/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/favicon.png -------------------------------------------------------------------------------- /step09/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/default_img.png -------------------------------------------------------------------------------- /step09/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/facebook_signin.png -------------------------------------------------------------------------------- /step09/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/google_signin.png -------------------------------------------------------------------------------- /step09/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/homescreen144.png -------------------------------------------------------------------------------- /step09/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/homescreen192.png -------------------------------------------------------------------------------- /step09/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/homescreen48.png -------------------------------------------------------------------------------- /step09/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/homescreen72.png -------------------------------------------------------------------------------- /step09/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/homescreen96.png -------------------------------------------------------------------------------- /step09/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /step09/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /step09/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /step09/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /step09/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /step09/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /step09/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/save-password.png -------------------------------------------------------------------------------- /step09/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /step09/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/step09/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /step09/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /step09/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var cmapiAvailable = !!window.PasswordCredential; 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | if (cmapiAvailable) { 71 | var profile = googleUser.getBasicProfile(); 72 | var cred = new FederatedCredential({ 73 | id: profile.getEmail(), 74 | name: profile.getName(), 75 | iconURL: profile.getImageUrl(), 76 | provider: GOOGLE_SIGNIN 77 | }); 78 | return navigator.credentials.store(cred); 79 | } else { 80 | return Promise.resolve(); 81 | } 82 | } else { 83 | return Promise.reject(); 84 | } 85 | }); 86 | }).then(function() { 87 | location.href = '/main?quote=You are signed in with Google SignIn'; 88 | }, function() { 89 | app.fire('show-toast', { 90 | text: 'Google Sign-In failed' 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /step09/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | var autoSignIn = function(mode) { 2 | if (cmapiAvailable) { 3 | return navigator.credentials.get({ 4 | password: true, 5 | federated: { 6 | providers: [ GOOGLE_SIGNIN ] 7 | }, 8 | mediation: mode 9 | }).then(function(cred) { 10 | if (cred) { 11 | var form = new FormData(); 12 | var csrf_token = document.querySelector('#csrf_token').value; 13 | form.append('csrf_token', csrf_token); 14 | 15 | switch (cred.type) { 16 | case 'password': 17 | form.append('email', cred.id); 18 | form.append('password', cred.password); 19 | return fetch('/auth/password', { 20 | method: 'POST', 21 | credentials: 'include', 22 | body: form 23 | }); 24 | 25 | case 'federated': 26 | switch (cred.provider) { 27 | case GOOGLE_SIGNIN: 28 | return gSignIn(cred.id) 29 | .then(function(googleUser) { 30 | var id_token = googleUser.getAuthResponse().id_token; 31 | form.append('id_token', id_token); 32 | return fetch('/auth/google', { 33 | method: 'POST', 34 | credentials: 'include', 35 | body: form 36 | }); 37 | }); 38 | } 39 | } 40 | return Promise.reject(); 41 | } else { 42 | return Promise.reject(); 43 | } 44 | }).then(function(res) { 45 | if (res.status === 200) { 46 | return Promise.resolve(); 47 | } else { 48 | return Promise.reject(); 49 | } 50 | }); 51 | } else { 52 | return Promise.reject(); 53 | } 54 | }; 55 | 56 | googleAuthReady.then(function() { 57 | return autoSignIn('silent'); 58 | }).then(function() { 59 | location.href = '/main?quote=You are automatically signed in'; 60 | }, function() { 61 | console.log('auto sign-in skipped'); 62 | }); 63 | -------------------------------------------------------------------------------- /step09/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // Initialize Google Sign-In 27 | var googleAuthReady = new Promise(function(resolve) { 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init().then(function() { 30 | resolve(); 31 | }); 32 | }); 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /step09/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(regForm); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are registered'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are registered'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Registration failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Registration failed' 28 | }); 29 | }); 30 | }); 31 | 32 | var signin = document.querySelector('#signin'); 33 | signin.addEventListener('click', function() { 34 | autoSignIn() 35 | .then(function() { 36 | location.href = '/main?quote=You are signed in'; 37 | }, function() { 38 | location.href = '/signin'; 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /step09/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | if (cmapiAvailable) { 4 | navigator.credentials.preventSilentAccess() 5 | .then(function() { 6 | location.href = '/signout'; 7 | }); 8 | } else { 9 | location.href = '/signout'; 10 | } 11 | }); 12 | 13 | var unregForm = document.querySelector('#unregForm'); 14 | unregForm.addEventListener('submit', function(e) { 15 | e.preventDefault(); 16 | 17 | fetch('/unregister', { 18 | method: 'POST', 19 | credentials: 'include', 20 | body: new FormData(unregForm) 21 | }).then(function(res) { 22 | if (res.status === 200) { 23 | if (cmapiAvailable) { 24 | navigator.credentials.preventSilentAccess() 25 | .then(function() { 26 | location.href = '/?quote=You are unregistered'; 27 | }); 28 | } else { 29 | location.href = '/?quote=You are unregistered'; 30 | } 31 | } else { 32 | app.fire('show-toast', { 33 | text: 'Unregister failed' 34 | }); 35 | } 36 | }, function() { 37 | app.fire('show-toast', { 38 | text: 'Unregister failed' 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /step09/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | if (cmapiAvailable) { 12 | var cred = new PasswordCredential(form); 13 | navigator.credentials.store(cred) 14 | .then(function() { 15 | location.href = '/main?quote=You are signed in'; 16 | }); 17 | } else { 18 | location.href = '/main?quote=You are signed in'; 19 | } 20 | } else { 21 | app.fire('show-toast', { 22 | text: 'Authentication failed' 23 | }); 24 | } 25 | }, function() { 26 | app.fire('show-toast', { 27 | text: 'Authentication failed' 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /step09/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 27 | 37 |
38 | 41 |
42 |
43 |
44 | or 45 |
46 |
47 | 48 |
49 |
50 |
{% endblock %} 51 | -------------------------------------------------------------------------------- /step09/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /step09/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /step09/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 19 | 29 |
30 | 33 |
34 |
35 |
36 | or 37 |
38 |
39 | 40 |
41 |
42 |
{% endblock %} 43 | -------------------------------------------------------------------------------- /working/app.yaml: -------------------------------------------------------------------------------- 1 | ../app.yaml -------------------------------------------------------------------------------- /working/client_secrets.json: -------------------------------------------------------------------------------- 1 | ../client_secrets.json -------------------------------------------------------------------------------- /working/lib: -------------------------------------------------------------------------------- 1 | ../lib -------------------------------------------------------------------------------- /working/static/bower_components: -------------------------------------------------------------------------------- 1 | ../../bower_components -------------------------------------------------------------------------------- /working/static/elements/elements.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /working/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/favicon.png -------------------------------------------------------------------------------- /working/static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/default_img.png -------------------------------------------------------------------------------- /working/static/images/facebook_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/facebook_signin.png -------------------------------------------------------------------------------- /working/static/images/google_signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/google_signin.png -------------------------------------------------------------------------------- /working/static/images/homescreen144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/homescreen144.png -------------------------------------------------------------------------------- /working/static/images/homescreen192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/homescreen192.png -------------------------------------------------------------------------------- /working/static/images/homescreen48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/homescreen48.png -------------------------------------------------------------------------------- /working/static/images/homescreen72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/homescreen72.png -------------------------------------------------------------------------------- /working/static/images/homescreen96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/homescreen96.png -------------------------------------------------------------------------------- /working/static/images/howto/add-second-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/add-second-account.png -------------------------------------------------------------------------------- /working/static/images/howto/auto-sign-in-with-2-accounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/auto-sign-in-with-2-accounts.png -------------------------------------------------------------------------------- /working/static/images/howto/auto-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/auto-sign-in.png -------------------------------------------------------------------------------- /working/static/images/howto/fb_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/fb_config.png -------------------------------------------------------------------------------- /working/static/images/howto/gsi_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/gsi_config.png -------------------------------------------------------------------------------- /working/static/images/howto/opt-in-to-smart-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/opt-in-to-smart-lock.png -------------------------------------------------------------------------------- /working/static/images/howto/save-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/save-password.png -------------------------------------------------------------------------------- /working/static/images/howto/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/sign-up.png -------------------------------------------------------------------------------- /working/static/images/howto/the-first-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlecodelabs/credential-management-api/b37a6126f0534e9c0a3a23fe8658ef11fc0b1e4d/working/static/images/howto/the-first-sign-in.png -------------------------------------------------------------------------------- /working/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Credential Management API Codelab", 3 | "short_name": "Credential API Codelab", 4 | "display": "standalone", 5 | "icons": [{ 6 | "src": "images/homescreen48.png", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | }, { 10 | "src": "images/homescreen72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, { 14 | "src": "images/homescreen96.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, { 18 | "src": "images/homescreen144.png", 19 | "sizes": "144x144", 20 | "type": "image/png" 21 | }, { 22 | "src": "images/homescreen192.png", 23 | "sizes": "192x192", 24 | "type": "image/png" 25 | }], 26 | "chrome_related_applications": [{ 27 | "platform": "web" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /working/static/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // TODO 4-1: Detect Credential Management API feature 19 | 20 | /* 21 | Although this sample app is using Polymer, most of the interactions are 22 | handled using regular APIs so you don't have to learn about it. 23 | */ 24 | var app = document.querySelector('#app'); 25 | // Set an event listener to show a toast. (Polymer) 26 | app.listeners = { 27 | 'show-toast': 'showToast' 28 | }; 29 | 30 | /** 31 | * Polymer event handler to show a toast. 32 | * @param {Event} e Polymer custom event object 33 | * @return {void} 34 | */ 35 | app.showToast = function(e) { 36 | this.$.toast.text = e.detail.text; 37 | this.$.toast.show(); 38 | }; 39 | 40 | app.addEventListener('dom-change', function() { 41 | var url = new URL(location.href); 42 | var params = new URLSearchParams(url.search.slice(1)); 43 | if (params.get('quote')) { 44 | app.fire('show-toast', { 45 | text: params.get('quote') 46 | }); 47 | } 48 | }); 49 | 50 | /** 51 | * When google sign-in button is pressed. 52 | * @return {void} 53 | */ 54 | var gsignin = document.querySelector('#gsignin'); 55 | gsignin.addEventListener('click', function() { 56 | gSignIn() 57 | .then(function(googleUser) { 58 | // Now user is successfully authenticated with Google. 59 | // Send ID Token to the server to authenticate with our server. 60 | var form = new FormData(); 61 | form.append('id_token', googleUser.getAuthResponse().id_token); 62 | form.append('csrf_token', document.querySelector('#csrf_token').value); 63 | 64 | return fetch('/auth/google', { 65 | method: 'POST', 66 | credentials: 'include', 67 | body: form 68 | }).then(function(res) { 69 | if (res.status === 200) { 70 | // TODO 9-1: Store a credential on successful sign-in using Google Sign-In 71 | return Promise.resolve(); 72 | } else { 73 | return Promise.reject(); 74 | } 75 | }); 76 | }).then(function() { 77 | location.href = '/main?quote=You are signed in with Google SignIn'; 78 | }, function() { 79 | app.fire('show-toast', { 80 | text: 'Google Sign-In failed' 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /working/static/scripts/auto.js: -------------------------------------------------------------------------------- 1 | // TODO 6-1: Define `autoSignIn()` function 2 | // TODO 7-1: Sign-In a user upon landing the page -------------------------------------------------------------------------------- /working/static/scripts/federation.js: -------------------------------------------------------------------------------- 1 | var PASSWORD_LOGIN = 'password'; 2 | var GOOGLE_SIGNIN = 'https://accounts.google.com'; 3 | var DEFAULT_IMG = location.origin+'/images/default_img.png'; 4 | 5 | /** 6 | * Let user sign-in using Google Sign-in 7 | * @param {String} id Preferred Gmail address for user to sign-in 8 | * @return {Promise} Returns result of authFlow 9 | */ 10 | var gSignIn = function(id) { 11 | var auth2 = gapi.auth2.getAuthInstance(); 12 | if (auth2.isSignedIn.get()) { 13 | // Check if currently signed in user is the same as intended. 14 | var googleUser = auth2.currentUser.get(); 15 | if (googleUser.getBasicProfile().getEmail() === id) { 16 | return Promise.resolve(googleUser); 17 | } 18 | } 19 | return auth2.signIn({ 20 | // Set `login_hint` to specify an intended user account, 21 | // otherwise user selection dialog will popup. 22 | login_hint: id || '' 23 | }); 24 | }; 25 | 26 | // TODO 9-2: Return a promise upon initializing Google Sign-In 27 | // Initialise Google Sign-In 28 | gapi.load('auth2', function() { 29 | gapi.auth2.init(); 30 | }); 31 | -------------------------------------------------------------------------------- /working/static/scripts/index.js: -------------------------------------------------------------------------------- 1 | var regForm = document.querySelector('#regForm'); 2 | regForm.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/register', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(regForm) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | // TODO 4-2: Store the credential upon successful registration 12 | location.href = '/main?quote=You are registered'; 13 | } else { 14 | app.fire('show-toast', { 15 | text: 'Registration failed' 16 | }); 17 | } 18 | }, function() { 19 | app.fire('show-toast', { 20 | text: 'Registration failed' 21 | }); 22 | }); 23 | }); 24 | 25 | var signin = document.querySelector('#signin'); 26 | signin.addEventListener('click', function() { 27 | // TODO 6-5: Sign-In a user by pressing a "Sign-In" button 28 | location.href = '/signin'; 29 | }); 30 | -------------------------------------------------------------------------------- /working/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var signout = document.querySelector('#signout'); 2 | signout.addEventListener('click', function() { 3 | // TODO 8-1: Turn off auto sign-in when a user signs out 4 | location.href = '/signout'; 5 | }); 6 | 7 | var unregForm = document.querySelector('#unregForm'); 8 | unregForm.addEventListener('submit', function(e) { 9 | e.preventDefault(); 10 | 11 | fetch('/unregister', { 12 | method: 'POST', 13 | credentials: 'include', 14 | body: new FormData(unregForm) 15 | }).then(function(res) { 16 | if (res.status === 200) { 17 | // TODO 8-2: Turn off auto sign-in when a user unregisters 18 | location.href = '/?quote=You are unregistered'; 19 | } else { 20 | app.fire('show-toast', { 21 | text: 'Unregister failed' 22 | }); 23 | } 24 | }, function() { 25 | app.fire('show-toast', { 26 | text: 'Unregister failed' 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /working/static/scripts/signin.js: -------------------------------------------------------------------------------- 1 | var form = document.querySelector('#form'); 2 | form.addEventListener('submit', function(e) { 3 | e.preventDefault(); 4 | 5 | fetch('/auth/password', { 6 | method: 'POST', 7 | credentials: 'include', 8 | body: new FormData(form) 9 | }).then(function(res) { 10 | if (res.status === 200) { 11 | // TODO 5-1: Store the credential upon successful sign-inVj 12 | location.href = '/main?quote=You are signed in'; 13 | } else { 14 | app.fire('show-toast', { 15 | text: 'Authentication failed' 16 | }); 17 | } 18 | }, function() { 19 | app.fire('show-toast', { 20 | text: 'Authentication failed' 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /working/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 18 | 25 | 34 |
35 | 38 |
39 |
40 |
41 | or 42 |
43 |
44 | 45 |
46 |
47 |
{% endblock %} 48 | -------------------------------------------------------------------------------- /working/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | Credential Management API Codelab 26 | 27 | 28 | 29 | 30 | 31 | {% if path == '/' or path == '/signin'%} 32 | 33 | {% endif %} 34 | 35 | 36 | 37 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Menu 104 | 105 | 106 | Home 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | Credential Management API Codelab 120 |
121 | 122 | 123 | {% if path == '/' %} 124 | Sign In 125 | {% elif path == '/main' %} 126 | Sign Out 127 | {% endif %} 128 |
129 | 130 | 131 |
132 | {% block body %}{% endblock %} 133 |
134 |
135 |
136 | 139 | 140 | 141 | {% if path == '/' or path == '/signin' %} 142 | 143 | 144 | {% endif %} 145 | {% if path == '/' %} 146 | 147 | {% endif %} 148 | {% if path == '/signin' %} 149 | 150 | {% endif %} 151 | {% if path == '/main' %} 152 | 153 | {% endif %} 154 | 155 | 156 | -------------------------------------------------------------------------------- /working/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |

Welcome! {{name}}

6 |
7 | 8 |
9 |
10 |
11 | 12 | 15 |
16 |
{% endblock %} 17 | -------------------------------------------------------------------------------- /working/templates/signin.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 | 5 |
6 | 11 | 18 | 27 |
28 | 31 |
32 |
33 |
34 | or 35 |
36 |
37 | 38 |
39 |
40 |
{% endblock %} 41 | --------------------------------------------------------------------------------