(function () { 'use strict'; window.ngOauth3App = angular.module('oauth3Playground', [ 'oauth3.org' ]) //window.ngOauth3App = angular.module('oauth3Playground', [ 'ui.router' ]) /* ngOauth3App.config(function($stateProvider) { var helloState = { name: 'hello', url: '/hello', template: '

hello world!

' } var aboutState = { name: 'about', url: '/about', template: '

Its the UI-Router hello world app!

' } $stateProvider.state(helloState); $stateProvider.state(aboutState); }); */ .controller('PlaygroundCtrl', [ '$timeout', 'azp@oauth3.org', function ($timeout, OAUTH3) { // NOTE: This OAUTH3 is the same as window.OAUTH3, but with angular's promise injected // TODO: how to load more than one version of oauth3 on the page (i.e. a vanilla version without angular entaglement) var vm = this; vm.framework = 'none'; vm.clientUri = OAUTH3.clientUri({ host: window.location.host }); vm.conf = { debug: undefined, client_id: vm.clientUri, client_uri: vm.clientUri, provider_uri: vm.clientUri }; vm.providerUri = vm.conf.client_uri; // map of things being debounced presently vm.debouncing = {}; vm.defaults = { provider: vm.conf.provider_uri, directives: null }; vm.defaults.scopes = [ { name: 'profile@oauth3.org', desc: "Basic profile information", checked: true } , { name: 'oauth3_authn', desc: "Basic secure authentication", checked: true } //{ name: 'authn@oauth3.org', desc: "Basic secure authentication" } , { name: 'photos@daplie.com', desc: "Access to photos" } , { name: 'profile@oauth3.org', desc: "Access to basic profile info such as username, display_name, etc" } , { name: 'dns', desc: "DNS records (A/AAAA, TXT, SRV, MX, etc)" } , { name: '*', desc: "FULL ACCOUNT ACCESS" } //, 'auth@oauth3.org': "Basic secure authentication" //, 'profile.email@oauth3.org': "Email address" //, 'profile.phone@oauth3.org': "Phone number" ]; vm.form = {}; vm.form.id = 'coolaj86@gmail.com'; vm.form.subject = ''; vm.form.userProvider = ''; vm.form.provider = 'sso.hellabit.com'; vm.form.scopes = ''; vm.locks = {}; vm.validated = {}; vm.responses = {}; // // Convenience for our app // vm.fn = {}; vm.fn.updateUrls = function () { Object.keys(vm.api.urls).forEach(function (key) { var fn = vm.api.urls[key]; fn(); }); }; vm.fn._debounce = {}; vm.fn.debounceUi = function () { if (vm.debouncing.user || vm.debouncing.provider) { vm.locks['login'] = true; } else { vm.locks['login'] = false; } }; vm.fn.debounce = function (name, time) { vm.debouncing[name] = true; vm.fn.debounceUi(); $timeout.cancel(vm.fn._debounce[name]); vm.fn._debounce[name] = $timeout(function () { vm.debouncing[name] = false; vm.fn.debounceUi(); // do nothing, just use promise return; }, time || 250); return vm.fn._debounce[name]; } vm.fn.changeUser = function () { var parts = vm.form.id.split('@'); var user; var provider; if (/@/.test(vm.form.id)) { // The username may have a single @, the provider may not // user@thing.com@whatever.com -> user@thing.com, whatever.com provider = parts.pop(); user = parts.join(''); } else { //vm.form.hasUser = false; user = ''; provider = parts.join(''); } vm.form.subject = vm.form.id; return vm.fn.debounce('provider', 250).then(function () { var parts = vm.form.provider.split('.'); if (!vm.form.providerIndependent) { vm.form.provider = provider; } vm.form.userProvider = provider; // Careful: don't use state within a debounce function // uses vm.form.provider for lookup if (parts.length >= 2 && parts[parts.length - 1].length >= 2 && parts.every(function (p) {return p.length})) { return vm.api.discover().then(function () { console.log('[changeUser] vm.directives:'); console.log(vm.directives); console.log(provider); console.log(OAUTH3.uri.normalize(vm.directives.issuer)); if (vm.directives && provider === OAUTH3.uri.normalize(vm.directives.issuer)) { vm.form.subject = user; } else { vm.form.subject = vm.form.id; } }); } }); }; vm.fn.changeProvider = function () { vm.form.providerIndependent = true; var parts = vm.form.provider.split('.'); vm.fn.debounce('provider', 250).then(function () { // Careful: don't use state within a debounce function if (parts.length >= 2 && parts[parts.length - 1].length >= 2 && parts.every(function (p) {return p.length})) { return vm.api.discover(); } }); }; vm.fn.toggleAdvanced = function () { vm.advanced = !vm.advanced; vm.form.provider = vm.form.userProvider; if (!vm.advanced) { vm.form.providerIndependent = false; vm.fn.changeUser(); } }; vm.fn.updateDebug = function () { if (!vm.conf.debug) { vm.conf.debug = undefined; } }; vm.fn.updateScopes = function () { var scopes = {}; (vm.scopes && vm.scopes.split(',') || []).forEach(function (name) { scopes[name] = true; }); vm.defaults.scopes.forEach(function (scope) { if (scope.checked) { scopes[scope.name] = true; } else { scopes[scope.name] = false; } }); vm.form.scopes = Object.keys(scopes).filter(function (key) { return scopes[key]; }).map(function (key) { return key; }).join(','); vm.fn.updateUrls(); // vm.api.urls.implicitGrant(); }; vm.fn.lock = function () { vm._working = true; }; vm.fn.unlock = function () { vm._working = false; }; vm.fn.clearError = function () { vm.error = null; }; vm.fn.clearDirectives = function () { vm.directives = null; }; // A place for all the generated urls vm.urls = {}; // // Wrap around the OAUTH3 APIs // vm.api = {}; vm.api.urls = {}; vm.api.authn = {}; vm.api.jwt = {}; vm.api.urls.credentialMeta = function () { if (!vm.directives ||!vm.directives.credential_meta || !vm.form.id) { return; } vm.urls.credentialMeta = OAUTH3.urls.credentialMeta(vm.directives, { email: vm.form.id }); }; vm.api.urls.otp = function () { if (!vm.directives || !vm.form.id) { return; } vm.urls.otp = OAUTH3.urls.otp(vm.directives, { email: vm.form.id }); }; vm.api.authn.otp = function () { vm.fn.updateUrls(); // vm.api.urls.otp(); OAUTH3.authn.otp(vm.directives, { email: vm.form.id }).then(function (resp) { vm.responses.otp = resp; vm.form.otpUuid = resp.data.code_id; console.log('vm.responses.otp: (' + typeof resp + ')'); console.log(vm.responses.otp); console.log('vm.form.otpUuid:'); console.log(vm.form.otpUuid); vm.fn.updateUrls(); // vm.api.urls.resourceOwnerPassword(); }); }; vm.api.authn.credentialMeta = function () { vm.fn.updateUrls(); // vm.api.urls.credentialMeta(); OAUTH3.authn.loginMeta(vm.directives, { email: vm.form.id }).then(function () { vm.fn.updateUrls(); // vm.api.urls.credentialMeta(); }); }; vm.api.authn._ropOpts = function () { //var opts = { email: vm.form.id, uuid: vm.form.otpUuid, code: vm.form.otpCode }; return vm.api.authn._ropOpts_ = { client_id: vm.conf.client_uid || undefined , client_uri: vm.conf.client_uri || undefined , grant_type: 'password' , username: vm.form.id || undefined , password: vm.form.otpCode || undefined , totp: vm.form.totpToken || undefined , otp: vm.form.otpCode || "{{otp-code}}" , password_type: vm.form.otpCode && 'otp' || undefined , otp_code: vm.form.otpCode || undefined , otp_id: vm.form.otpUuid || undefined , otp_uuid: vm.form.otpUuid || undefined , user_agent: navigator.userAgent || undefined // "AJ's Macbook" for a specific device? , jwk: vm.form.rememberDevice && opts.jwk || undefined //, "public_key": opts.rememberDevice && opts.publicKey || undefined //, "public_key_type": opts.rememberDevice && opts.publicKeyType || undefined // RSA/ECDSA //, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key , debug: vm.form.debug || undefined }; }; vm.api.urls.resourceOwnerPassword = function () { if (!vm.directives || !vm.form.otpUuid) { return; } vm.urls.resourceOwnerPassword = OAUTH3.urls.resourceOwnerPassword(vm.directives, vm.api.authn._ropOpts()); }; vm.api.authn.resourceOwnerPassword = function () { vm.fn.updateUrls(); // vm.api.urls.resourceOwnerPassword(); OAUTH3.authn.resourceOwnerPassword(vm.directives, vm.api.authn._ropOpts()).then(function (resp) { vm.responses.resourceOwnerPassword = { status: 0, data: resp }; vm.form.accessToken = vm.accessToken = resp.access_token; vm.form.refreshToken = vm.refreshToken = resp.refresh_token; vm.ropSession = resp; vm.ropToken = resp.token; vm.fn.updateUrls(); // vm.api.urls.resourceOwnerPassword(); also grants }); }; vm.api.jwt.decode = function () { vm.ropToken = OAUTH3.jwt.decode(vm.form.accessToken || vm.accessToken); }; vm.api.jwt.decodeRefresh = function () { vm.ropToken = OAUTH3.jwt.decode(vm.form.refreshToken || vm.refreshToken); }; vm.api.providerUri = function () { console.log('[DEBUG] providerUri:', vm.providerUri); try { vm.providerUri = OAUTH3.uri.normalize(vm.providerUri); vm.conf.provider_uri = vm.providerUri; } catch(e) { vm.error = e; } }; vm.api.clientUri = function () { console.log('[DEBUG] clientUri:', vm.clientUri); try { vm.clientUri = OAUTH3.clientUri({ host: vm.clientUri }); if (vm.clientUri) { console.log('[DEBUG] clientUri:', vm.clientUri); vm.conf.client_uri = vm.clientUri; vm.conf.client_id = vm.clientUri; } } catch(e) { vm.error = e; } }; vm.api._discoverCount = 0; vm.api.urls.implicitGrant = function (provider) { if (!vm.directives) { console.log('[DEBUG] skipping implicit grant due to missing directives'); return; } var opts = { client_uri: vm.conf.client_uri , subject: vm.form.subject || undefined , debug: vm.conf.debug || undefined , scope: vm.form.scopes || undefined }; var implicitGrantObj = OAUTH3.urls.implicitGrant(vm.directives, opts); vm.urls.implicitGrant = vm.implicitGrantUrl = (OAUTH3.url.normalize(provider || vm.form.provider) + '/' + implicitGrantObj.url).replace(implicitGrantObj.state, '{{random}}'); } vm.api.discover = function () { vm.directives = null; vm.validated.provider = ''; vm.api._discoverCount += 1; var latest = vm.api._discoverCount; var provider = vm.form.provider; // shouldn't be mutable during this time but... vm.fn.lock(); vm.discoveryObj = OAUTH3.urls.discover(provider, vm.conf); vm.urls.directives = vm.directivesUrl = OAUTH3.url.normalize(provider) + '/' + vm.discoveryObj.query._pathname; vm.urls.discovery = vm.discoveryUrl = vm.discoveryObj.method + ' ' + vm.discoveryObj.url; console.log('about to discover'); return OAUTH3.discover(provider, vm.conf).then(function (dir) { if (latest !== vm.api._discoverCount) { console.log('[DEBUG] ignoring stale discover response for', provider); return; } console.log('[DEBUG] directives:'); console.log(dir); vm.validated.provider = provider; vm.directives = dir; vm.api.urls.implicitGrant(provider); //JSON.stringify(dir, null, 2); }, function (err) { vm.form.provider = vm.defaults.provider; vm.validated.provider = vm.defaults.provider; vm.directives = vm.defaults.directives; if (latest !== vm.api._discoverCount) { console.warn('[DEBUG] ignoring stale discover error for', provider); console.warn(err); return; } console.log('error on discover'); vm.error = err; }).then(function () { vm.fn.unlock(); }); }; vm.api.urls.scope = function () { // TODO do something like this in the oauth3 library vm.urls.scope = 'https://:host/.well-known/oauth3/scopes/:scope.json\nhttps://example.com/.well-known/oauth3/scopes/example@oauth3.org.json\n\n' + 'GET https://example.com/.well-known/oauth3/#/?action=directives&state={{random}}&redirect_uri=https%3A%2F%2Fsso.hellabit.com%2F.well-known%2Foauth3%2Fcallback.html%23%2F&response_type=rpc&_method=GET&_pathname=.well-known%2Foauth3%2Fscopes%2Fexample@oauth3.org.json'; }; vm.api.discoverScopes = function () { var scopes = vm.form.scopes && vm.form.scopes.split(',') || []; vm.scopesObj = []; function nextScope() { var scopename = scopes.shift(); if (!scopename) { return; } // something like https://example.com/.well-known/oauth3.org/scopes/:scopename.json var scopeUrlObj = OAUTH3.urls.discoverScope(vm.form.provider, { client_uri: vm.conf.client_uri , scope: scopename , debug: vm.conf.debug || undefined }); vm.urls.scope = vm.scopeUrl = OAUTH3.url.normalize(provider) + '/' + scopeUrlObj.query._pathname; // something like the discovery url that loads in an iframe var discoverScopeObj = OAUTH3.urls.discoverScope(vm.form.provider, { client_uri: vm.conf.client_uri , scope: scopename , debug: vm.conf.debug || undefined }); vm.urls.discoverScope = vm.discoverScopeUrl = OAUTH3.url.normalize(provider) + '/' + discoverScopeObj.url; // Go and fetch! return OAUTH3.discoverScopes(vm.form.provider, { client_uri: vm.conf.client_uri , scope: scopename , debug: vm.conf.debug || undefined }).then(function (scope) { var allScopes = {}; vm.scopesObj.push(scope); vm.defaults.scopes.push(scope); vm.defaults.scopes = vm.defaults.scopes.filter(function (scope) { if (allScopes[scope.name]) { return false; } allScopes[scope.name] = true; return true; }); }, function (err) { console.error("Error in discover scope:"); console.error(err); vm.scopesObj.push({ name: scopename, desc: "Error, not found" }); }); } return nextScope(); }; vm.api.implicitGrant = function () { var provider = vm.validated.provider; var opts = { client_uri: vm.conf.client_uri , subject: vm.form.subject || undefined , debug: vm.conf.debug || undefined , scope: vm.form.scopes || undefined }; console.log('[DEBUG] vm.directives'); console.log(vm.directives); vm.implicitGrantObj = OAUTH3.urls.implicitGrant(vm.directives, opts); console.log('[DEBUG] vm.implicitGrantObj'); console.log(vm.implicitGrantObj); vm.urls.implicitGrant = vm.implicitGrantUrl = (OAUTH3.url.normalize(provider) + '/' + vm.implicitGrantObj.url); return OAUTH3.implicitGrant(vm.directives, opts).then(function (session) { vm.session = session; }); }; vm.urls.profile = {}; vm.api.urls.profile_get = function () { if (!vm.directives || !vm.accessToken) { return; } vm.urls.profile_get = OAUTH3.urls.accounts.get(vm.directives, vm.ropSession); }; vm.api.profile = {}; vm.api.profile_get = function () { console.log('you tickled me!', vm.ropSession); vm.api.urls.profile_get(); return OAUTH3.requests.accounts.get(vm.directives, vm.ropSession).then(function (resp) { console.log('you tickled me twice!'); if (!resp.data) { resp = { status: 0, data: resp }; } vm.responses.profile_get = resp; }, function (err) { console.error('Could not get profile:'); console.error(err); vm.error = err; }); }; vm.api.profile_set = function () { console.log('you tickled me!', vm.ropSession); vm.api.urls.profile_set(); return OAUTH3.requests.accounts.set(vm.directives, vm.ropSession).then(function (resp) { console.log('you tickled me twice!'); if (!resp.data) { resp = { status: 0, data: resp }; } vm.responses.profile_set = resp; }, function (err) { console.error('Could not set profile:'); console.error(err); vm.error = err; }); }; vm.api.authz = {}; vm.api.authz._grantsOpts = function () { return vm.api.authz._grantsOpts_ = { method: 'GET' , client_id: vm.conf.client_id , client_uri: vm.conf.client_uri , session: vm.ropSession , debug: vm.conf.debug , all: true }; }; vm.api.urls.grants = function () { if (!vm.directives || !vm.ropSession || !vm.form.id) { return; } vm.urls.grants = OAUTH3.urls.grants(vm.directives, vm.api.authz._grantsOpts()); }; vm.api.authz.grants = function () { vm.fn.updateUrls(); // vm.api.urls.grants(); return OAUTH3.authz.grants(vm.form.provider, vm.api.authz._grantsOpts()).then(function (resp) { vm.responses.grants = { status: 0, data: resp }; vm.fn.updateUrls(); // vm.api.urls.grants(); }, function (err) { console.error('[error] authz.grants:'); console.error(err); vm.error = err; }); }; vm.form.provider = vm.defaults.provider; vm.validated.provider = vm.defaults.provider; vm.api.discover().then(function () { vm.defaults.directives = vm.directives; }); vm.fn.updateScopes(); vm.apistr = ''; Object.keys(OAUTH3).forEach(function (key) { var thingy = OAUTH3[key]; if ('_' === key[0] || -1 !== [ 'create', '_browser', '_defaultStorage', 'hooks', '_hooks', '_digest' ].indexOf(key)) { return; } if ('function' === typeof thingy) { vm.apistr += thingy.toString().split(/\n/)[0].replace('function ', 'OAUTH3.' + key).replace(/\s+{\s*/, '') + '\n'; } if ('object' === typeof thingy) { Object.keys(thingy).forEach(function (key2) { var thingy2 = thingy[key2]; if ('function' === typeof thingy2) { vm.apistr += thingy2.toString().split(/\n/)[0].replace('function ', 'OAUTH3.' + key + '.' + key2).replace(/\s+{\s*/, '') + '\n'; } if ('object' === typeof thingy2) { Object.keys(thingy2).forEach(function (key3) { var thingy3 = thingy2[key3]; if ('function' === typeof thingy3) { vm.apistr += thingy3.toString().split(/\n/)[0].replace('function ', 'OAUTH3.' + key + '.' + key2 + '.' + key3).replace(/\s+{\s*/, '') + '\n'; } }); } }); } }); } ] ); }());