/* global Promise */ ;(function (exports) { 'use strict'; OAUTH3.utils.query.parse = function (search) { // parse a query or a hash if (-1 !== ['#', '?'].indexOf(search[0])) { search = search.substring(1); } // Solve for case of search within hash // example: #/authorization_dialog/?state=...&redirect_uri=... var queryIndex = search.indexOf('?'); if (-1 !== queryIndex) { search = search.substr(queryIndex + 1); } var args = search.split('&'); var argsParsed = {}; var i, arg, kvp, key, value; for (i = 0; i < args.length; i += 1) { arg = args[i]; if (-1 === arg.indexOf('=')) { argsParsed[decodeURIComponent(arg).trim()] = true; } else { kvp = arg.split('='); key = decodeURIComponent(kvp[0]).trim(); value = decodeURIComponent(kvp[1]).trim(); argsParsed[key] = value; } } return argsParsed; }; OAUTH3.urls.resourceOwnerPassword = function (directive, opts) { // // Example Resource Owner Password Request // (generally for 1st party and direct-partner mobile apps, and webapps) // // POST https://example.com/api/org.oauth3.provider/access_token // { "grant_type": "password", "client_id": "<>", "scope": "<>" // , "username": "<>", "password": "password" } // opts = opts || {}; var type = 'access_token'; var grantType = 'password'; if (!opts.password) { if (opts.otp) { // for backwards compat opts.password = opts.otp; // 'otp:' + opts.otpUuid + ':' + opts.otp; } } var scope = opts.scope || directive.authn_scope; var clientId = opts.appId || opts.clientId || opts.client_id; var clientAgreeTos = opts.clientAgreeTos || opts.client_agree_tos; var clientUri = opts.clientUri || opts.client_uri || opts.clientUrl || opts.client_url; var args = directive[type]; var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined; var params = { "grant_type": grantType , "username": opts.username , "password": opts.password || otpCode || undefined , "totp": opts.totp || opts.totpToken || opts.totp_token || undefined , "otp": otpCode , "password_type": otpCode && 'otp' , "otp_code": otpCode , "otp_uuid": opts.otpUuid || opts.otp_uuid || undefined , "user_agent": opts.userAgent || opts.useragent || opts.user_agent || undefined // AJ's Macbook , "jwk": (opts.rememberDevice || opts.remember_device) && 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: opts.debug || undefined }; var uri = args.url; var body; if (opts.totp) { params.totp = opts.totp; } if (clientId) { params.clientId = clientId; } if (clientUri) { params.clientUri = clientUri; params.clientAgreeTos = clientAgreeTos; if (!clientAgreeTos) { throw new Error('Developer Error: missing clientAgreeTos uri'); } } if (scope) { params.scope = core.stringifyscope(scope); } if ('GET' === args.method.toUpperCase()) { uri += '?' + core.querystringify(params); } else { body = params; } return { url: uri , method: args.method , data: body }; }; OAUTH3.authz = {}; OAUTH3.authz.loginMeta = function (directive, opts) { if (opts.mock) { if (opts.mockError) { return OAUTH3.PromiseA.resolve({data: {error: {message: "Yikes!", code: 'E'}}}); } return OAUTH3.PromiseA.resolve({data: {}}); } return OAUTH3.request({ method: directive.credential_meta.method || 'GET' // TODO lint urls , url: OAUTH3.utils.url.resolve(directive.issuer, directive.credential_meta.url) .replace(':type', 'email') .replace(':id', opts.email) }); }; OAUTH3.authz.otp = function (directive, opts) { if (opts.mock) { if (opts.mockError) { return OAUTH3.PromiseA.resolve({data: {error: {message: "Yikes!", code: 'E'}}}); } return OAUTH3.PromiseA.resolve({data: {uuid: "uuidblah"}}); } return OAUTH3.request({ method: directive.credential_otp.url.method || 'POST' , url: OAUTH3.utils.url.resolve(directive.issuer, directive.credential_otp.url) , data: { // TODO replace with signed hosted file client_agree_tos: 'oauth3.org/tos/draft' , client_id: directive.issuer // In this case, the issuer is its own client , client_uri: directive.issuer , request_otp: true , username: opts.email } }); }; OAUTH3.authz.resourceOwnerPassword = function (directive, opts) { var providerUri = directive.issuer; if (opts.mock) { if (opts.mockError) { return OAUTH3.PromiseA.resolve({data: {error_description: "fake error", error: "errorcode", error_uri: "https://blah"}}); } //core.jwt.encode({header: {alg: 'none'}, payload: {exp: Date.now() / 1000 + 900, sub: 'fakeUserId'}, signature: "fakeSig" }) return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session.refresh( opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri } , { access_token: "eyJhbGciOiJub25lIn0.eyJleHAiOjE0ODc2MTQyMzUuNzg3LCJzdWIiOiJmYWtlVXNlcklkIn0.fakeSig" , refresh_token: "eyJhbGciOiJub25lIn0.eyJleHAiOjE0ODc2MTQyMzUuNzg3LCJzdWIiOiJmYWtlVXNlcklkIn0.fakeSig" , expires_in: "900" } )); } //var scope = opts.scope; //var appId = opts.appId; return oauth3.discover(providerUri, opts).then(function (directive) { var prequest = core.urls.resourceOwnerPassword(directive, opts); return oauth3.request(prequest).then(function (req) { var data = (req.originalData || req.data); data.provider_uri = providerUri; if (data.error) { return oauth3.PromiseA.reject(oauth3.core.formatError(providerUri, data.error)); } return oauth3.hooks.refreshSession( opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri } , data ); }); }); }; OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, scopes) { console.info('redirectWithToken scopes'); console.log(scopes); scopes.new = scopes.new || []; if ('token' === clientParams.response_type) { // get token and redirect client-side return OAUTH3.requests.grants(providerUri, { method: 'POST' , client_id: clientParams.client_uri , client_uri: clientParams.client_uri , scope: scopes.granted.concat(scopes.new).join(',') , response_type: clientParams.response_type , referrer: clientParams.referrer , session: session , debug: clientParams.debug }).then(function (results) { console.info('generate token results'); console.info(results); OAUTH3.redirect(clientParams, scopes, results); }); } else if ('code' === clientParams.response_type) { // get token and redirect server-side // (requires insecure form post as per spec) //OAUTH3.requests.authorizationDecision(); window.alert("Authorization Code Redirect NOT IMPLEMENTED"); throw new Error("Authorization Code Redirect NOT IMPLEMENTED"); } }; OAUTH3.requests = {}; OAUTH3.requests.accounts = {}; OAUTH3.requests.accounts.update = function (directive, session, opts) { var dir = directive.update_account || { method: 'POST' , url: 'https://' + directive.provider_url + '/api/org.oauth3.provider/accounts/:accountId' , bearer: 'Bearer' }; var url = dir.url .replace(/:accountId/, opts.accountId) ; return OAUTH3.request({ method: dir.method || 'POST' , url: url , headers: { 'Authorization': (dir.bearer || 'Bearer') + ' ' + session.accessToken } , json: { name: opts.name , comment: opts.comment , displayName: opts.displayName , priority: opts.priority } }); }; OAUTH3.requests.accounts.create = function (directive, session, account) { var dir = directive.create_account || { method: 'POST' , url: 'https://' + directive.issuer + '/api/org.oauth3.provider/accounts' , bearer: 'Bearer' }; var data = { // TODO fix the server to just use one scheme // account = { nick, self: { comment, username } } // account = { name, comment, display_name, priority } account: { nick: account.display_name , name: account.name , comment: account.comment , display_name: account.display_name , priority: account.priority , self: { nick: account.display_name , name: account.name , comment: account.comment , display_name: account.display_name , priority: account.priority } } , logins: [ { token: session.access_token } ] }; return OAUTH3.request({ method: dir.method || 'POST' , url: dir.url , session: session , data: data }); }; }('undefined' !== typeof exports ? exports : window));