Esempio n. 1
0
            options.loadGrantFunc(ticket.grant, function (err, grant, ext) {

                if (err ||
                    !grant ||
                    (grant.app !== ticket.app && grant.app !== ticket.dlg) ||
                    grant.user !== ticket.user ||
                    !grant.exp ||
                    grant.exp <= Hawk.utils.now()) {

                    return callback(err || Hawk.utils.unauthorized('Invalid grant'));
                }

                return reissue(ticket, app, grant, ext);
            });
Esempio n. 2
0
        Ticket.parse(id, encryptionPassword, options, function (err, ticket) {

            if (err) {
                return callback(err);
            }

            // Check expiration

            if (ticket.exp <= Hawk.utils.now()) {
                return callback(Hawk.utils.unauthorized('Expired ticket'));
            }

            return callback(null, ticket);
        });
Esempio n. 3
0
File: endpoints.js Progetto: rs22/oz
                options.loadAppFunc(payload.delegateTo, (err, delegatedApp) => {

                    if (err) {
                        return callback(err);
                    }

                    if (!delegatedApp) {
                        return callback(Hawk.utils.unauthorized('Invalid application'));
                    }

                    options.loadGrantFunc(ticket.grant, (err, grant, ext) => {

                        if (err) {
                            return callback(err);
                        }

                        if (!grant ||
                            grant.app !== ticket.app ||
                            grant.user !== ticket.user ||
                            !grant.exp ||
                            grant.exp <= Hawk.utils.now()) {

                            return callback(Hawk.utils.unauthorized('Invalid grant'));
                        }

                        return delegate(ticket, delegatedApp, grant);
                    });
                });
Esempio n. 4
0
exports.rsvp = function (app, grant, encryptionPassword, options) {

    options = options || {};

    if (!app || !app.id) {
        throw Boom.internal('Invalid application object');
    }

    if (!grant || !grant.id) {
        throw Boom.internal('Invalid grant object');
    }

    if (!encryptionPassword) {
        throw Boom.internal('Invalid encryption password');
    }

    options.ttl = options.ttl || internals.defaults.rsvpTTL;

    // Construct envelope

    const envelope = {
        app: app.id,
        exp: Hawk.utils.now() + options.ttl,
        grant: grant.id
    };

    // Stringify and encrypt

    return Iron.seal(envelope, encryptionPassword, options.iron || Iron.defaults);
};
Esempio n. 5
0
File: ticket.js Progetto: joshg/oz
exports.issue = function (app, grant, encryptionPassword, options, callback) {

    Hoek.toss(app && app.id, Boom.internal('Invalid application object'), callback);
    Hoek.toss(!grant || (grant.id && grant.user && grant.exp), Boom.internal('Invalid grant object'), callback);
    Hoek.toss(encryptionPassword, Boom.internal('Invalid encryption password'), callback);
    Hoek.toss(options, Boom.internal('Invalid options object'), callback);

    var scope = options.scope || (grant ? grant.scope : null) || app.scope || [];
    Hoek.toss(Scope.validate(scope), callback);

    // Construct ticket

    var exp = (Hawk.utils.now() + (options.ttl || exports.settings.ttl));
    if (grant) {
        exp = Math.min(exp, grant.exp);
    }

    var ticket = {
        exp: exp,
        app: app.id,
        scope: scope
    };

    if (options.ext) {
        ticket.ext = options.ext;
    }

    if (grant) {
        ticket.grant = grant.id;
        ticket.user = grant.user;
    }

    exports.generate(ticket, encryptionPassword, callback);
};
Esempio n. 6
0
File: ticket.js Progetto: joshg/oz
exports.rsvp = function (app, grant, encryptionPassword, options, callback) {

    Hoek.toss(app && app.id, Boom.internal('Invalid application object'), callback);
    Hoek.toss(grant && grant.id, Boom.internal('Invalid grant object'), callback);
    Hoek.toss(encryptionPassword, Boom.internal('Invalid encryption password'), callback);
    Hoek.toss(options, Boom.internal('Invalid options object'), callback);

    options.ttl = options.ttl || 1 * 60 * 1000                                  // 1 minute

    // Construct envelope

    var envelope = {
        app: app.id,
        exp: Hawk.utils.now() + options.ttl,
        grant: grant.id
    };

    // Stringify and encrypt

    Iron.seal(envelope, encryptionPassword, exports.settings, function (err, sealed) {

        if (err) {
            return callback(err);
        }

        var rsvp = sealed;
        return callback(null, rsvp);
    });
};
Esempio n. 7
0
exports._authenticate = async function (req, encryptionPassword, checkExpiration, options) {

    options = options || {};

    Hoek.assert(encryptionPassword, 'Invalid encryption password');

    // Hawk credentials lookup method

    const credentialsFunc = async function (id) {

        // Parse ticket id

        const ticket = await Ticket.parse(id, encryptionPassword, options.ticket);

        // Check expiration

        if (checkExpiration &&
            ticket.exp <= Hawk.utils.now()) {

            const error = Hawk.utils.unauthorized('Expired ticket');
            error.output.payload.expired = true;
            throw error;
        }

        return ticket;
    };

    // Hawk authentication

    const { credentials, artifacts } = await Hawk.server.authenticate(req, credentialsFunc, options.hawk);

    // Check application

    if (credentials.app !== artifacts.app) {
        throw Hawk.utils.unauthorized('Mismatching application id');
    }

    if ((credentials.dlg || artifacts.dlg) &&
        credentials.dlg !== artifacts.dlg) {

        throw Hawk.utils.unauthorized('Mismatching delegated application id');
    }

    // Return result

    return { ticket: credentials, artifacts };
};
Esempio n. 8
0
File: endpoints.js Progetto: rs22/oz
        Ticket.parse(payload.rsvp, options.encryptionPassword, options.ticket || {}, (err, envelope) => {

            if (err) {
                return callback(err);
            }

            if (envelope.app !== ticket.app) {
                return callback(Boom.forbidden('Mismatching ticket and rsvp apps'));
            }

            const now = Hawk.utils.now();

            if (envelope.exp <= now) {
                return callback(Boom.forbidden('Expired rsvp'));
            }

            options.loadGrantFunc(envelope.grant, (err, grant, ext) => {

                if (err) {
                    return callback(err);
                }

                if (!grant ||
                    (grant.app !== ticket.app && (!envelope.delegate || grant.app !== envelope.delegate.dlg)) ||
                    !grant.exp ||
                    grant.exp <= now) {

                    return callback(Boom.forbidden('Invalid grant'));
                }

                options.loadAppFunc(ticket.app, (err, app) => {

                    if (err) {
                        return callback(err);
                    }

                    if (!app) {
                        return callback(Boom.forbidden('Invalid application'));
                    }

                    const ticketOptions = Hoek.shallow(options.ticket || {});

                    if (ext) {
                        ticketOptions.ext = ext;
                    }

                    if (envelope.delegate) {
                        ticketOptions.dlg = envelope.delegate.dlg;

                        if (envelope.delegate.scope) {
                            ticketOptions.scope = envelope.delegate.scope;
                        }
                    }

                    Ticket.issue(app, grant, options.encryptionPassword, ticketOptions, callback);
                });
            });
        });
Esempio n. 9
0
    const credentialsFunc = async function (id) {

        // Parse ticket id

        const ticket = await Ticket.parse(id, encryptionPassword, options.ticket);

        // Check expiration

        if (checkExpiration &&
            ticket.exp <= Hawk.utils.now()) {

            const error = Hawk.utils.unauthorized('Expired ticket');
            error.output.payload.expired = true;
            throw error;
        }

        return ticket;
    };
Esempio n. 10
0
File: ticket.js Progetto: rs22/oz
exports.rsvp = function (app, grant, encryptionPassword, options, callback) {

    const fail = Hoek.nextTick(callback);

    if (!app || !app.id) {
        return fail(Boom.internal('Invalid application object'));
    }

    if (!grant || !grant.id) {
        return fail(Boom.internal('Invalid grant object'));
    }

    if (!encryptionPassword) {
        return fail(Boom.internal('Invalid encryption password'));
    }

    if (!options) {
        return fail(Boom.internal('Invalid options object'));
    }

    options.ttl = options.ttl || internals.defaults.rsvpTTL;

    // Construct envelope

    const envelope = {
        app: app.id,
        exp: Hawk.utils.now() + options.ttl,
        grant: grant.id
    };

    // Add options for delegation rsvp

    if (options.dlg) {

        const delegateOptions = { dlg: options.dlg };

        if (options.scope) {
            delegateOptions.scope = options.scope;
        }

        envelope.delegate = delegateOptions;
    }

    // Stringify and encrypt

    Iron.seal(envelope, encryptionPassword, options.iron || Iron.defaults, (err, sealed) => {

        if (err) {
            return callback(err);
        }

        const rsvp = sealed;
        return callback(null, rsvp);
    });
};
Esempio n. 11
0
    Hawk.server.authenticate(req, exports.credentialsFunc(encryptionPassword, options), options.hawk || {}, function (err, credentials, artifacts) {

        if (err) {
            return callback(err);
        }

        // Check application

        if (credentials.app !== artifacts.app) {
            return callback(Hawk.utils.unauthorized('Mismatching application id'));
        }

        if ((credentials.dlg || artifacts.dlg) &&
            credentials.dlg !== artifacts.dlg) {

            return callback(Hawk.utils.unauthorized('Mismatching delegated application id'));
        }

        // Return result

        return callback(null, credentials, artifacts);
    });
Esempio n. 12
0
exports.issue = function (app, grant, encryptionPassword, options) {

    options = options || {};

    if (!app || !app.id) {
        throw Boom.internal('Invalid application object');
    }

    if (grant && (!grant.id || !grant.user || !grant.exp)) {
        throw Boom.internal('Invalid grant object');
    }

    if (!encryptionPassword) {
        throw Boom.internal('Invalid encryption password');
    }

    const scope = (grant && grant.scope) || app.scope || [];
    Scope.validate(scope);

    if (grant &&
        grant.scope &&
        app.scope &&
        !Scope.isSubset(app.scope, grant.scope)) {

        throw Boom.internal('Grant scope is not a subset of the application scope');
    }

    // Construct ticket

    let exp = (Hawk.utils.now() + (options.ttl || internals.defaults.ticketTTL));
    if (grant) {
        exp = Math.min(exp, grant.exp);
    }

    const ticket = {
        exp,
        app: app.id,
        scope
    };

    if (grant) {
        ticket.grant = grant.id;
        ticket.user = grant.user;
    }

    if (options.delegate === false) {           // Defaults to true
        ticket.delegate = false;
    }

    return exports.generate(ticket, encryptionPassword, options);
};
Esempio n. 13
0
    Server.authenticate(req, options.encryptionPassword, options.hawk || {}, function (err, ticket, ext) {

        if (err) {
            return callback(err);
        }

        // Entity: any

        if (entity === 'any') {
            return callback(null, ticket);
        }

        // Entity: required

        if (entity === 'user') {
            if (!ticket.user) {
                return callback(Hawk.utils.unauthorized('Application ticket cannot be used on a user endpoint'));
            }

            return callback(null, ticket);
        }

        // Entity: none

        if (entity === 'app') {
            if (ticket.user) {
                return callback(Hawk.utils.unauthorized('User ticket cannot be used on an application endpoint'));
            }

            return callback(null, ticket);
        }

        // Entity: unknown

        return callback(Boom.internal('Unknown endpoint entity mode'));
    });
        }, function(clientId, callback) {
          let ext = undefined;

          // Parse authorization header for ext
          let attrs = hawk.utils.parseAuthorizationHeader(
            req.authorization
          );
          // Extra ext
          if (!(attrs instanceof Error)) {
            ext = attrs.ext;
          }

          // Get credentials with ext
          loadCredentials(clientId, ext, callback);
        }, {
Esempio n. 15
0
File: ticket.js Progetto: av4me/oz
        it('should construct a valid ticket', function (done) {

            var encryptionPassword = '******';

            var app = {
                id: '123'
            };

            var grant = {
                id: 's81u29n1812',
                user: '******',
                exp: Hawk.utils.now() + 5000,
                scope: ['a', 'b']
            };

            var options = {
                ttl: 10 * 60 * 1000,
                scope: ['b'],
                ext: {
                    x: 'welcome',
                    'private': 123
                }
            };

            Oz.ticket.issue(app, grant, encryptionPassword, options, function (err, envelope) {

                expect(err).to.not.exist;
                expect(envelope.ext.x).to.equal('welcome');
                expect(envelope.exp).to.equal(grant.exp);
                expect(envelope.ext.private).to.not.exist;

                Oz.ticket.parse(envelope.id, encryptionPassword, function (err, ticket) {

                    expect(err).to.not.exist;
                    expect(ticket.ext.x).to.equal('welcome');
                    expect(ticket.ext.private).to.equal(123);

                    Oz.ticket.reissue(ticket, encryptionPassword, {}, function (err, envelope2) {

                        expect(envelope2.ext.x).to.equal('welcome');
                        expect(envelope2.id).to.not.equal(envelope.id);
                        done();
                    });
                });
            });
        });
Esempio n. 16
0
File: endpoints.js Progetto: rs22/oz
            options.loadAppFunc(ticket.app, (err, app) => {

                if (err) {
                    return callback(err);
                }

                if (!app) {
                    return callback(Hawk.utils.unauthorized('Invalid application'));
                }

                if (payload.issueTo &&
                    !app.delegate) {

                    return callback(Boom.forbidden('Application has no delegation rights'));
                }

                // Application ticket

                if (!ticket.grant) {
                    return reissue(ticket, app);
                }

                // User ticket

                options.loadGrantFunc(ticket.grant, (err, grant, ext) => {

                    if (err) {
                        return callback(err);
                    }

                    if (!grant ||
                        (grant.app !== ticket.app && grant.app !== ticket.dlg) ||
                        grant.user !== ticket.user ||
                        !grant.exp ||
                        grant.exp <= Hawk.utils.now()) {

                        return callback(Hawk.utils.unauthorized('Invalid grant'));
                    }

                    return reissue(ticket, app, grant, ext);
                });
            });
Esempio n. 17
0
        Ticket.parse(payload.rsvp, options.encryptionPassword, options, function (err, envelope) {

            if (err) {
                return callback(err);
            }

            if (envelope.app !== ticket.app) {
                return callback(Boom.forbidden('Mismatching ticket and rsvp apps'));
            }

            var now = Hawk.utils.now();

            if (envelope.exp <= now) {
                return callback(Boom.forbidden('Expired rsvp'));
            }

            options.loadGrantFunc(envelope.grant, function (err, grant, ext) {

                if (err ||
                    !grant ||
                    grant.app !== ticket.app ||
                    !grant.exp ||
                    grant.exp <= now) {

                    return callback(err || Boom.forbidden('Invalid grant'));
                }

                options.loadAppFunc(grant.app, function (err, app) {

                    if (err || !app) {
                        return callback(err || Boom.forbidden('Invalid application'));
                    }

                    var ticketOptions = Hoek.clone(options.ticket) || {};
                    if (ext) {
                        ticketOptions.ext = ext;
                    }

                    Ticket.issue(app, grant, options.encryptionPassword, ticketOptions, callback);
                });
            });
        });
Esempio n. 18
0
    Server.authenticate(req, options.encryptionPassword, options, function (err, credentials, artifacts) {

        if (err) {
            return callback(err);
        }

        // Entity: app

        if (entity === 'app') {
            if (credentials.user) {
                return callback(Hawk.utils.unauthorized('User ticket cannot be used on an application endpoint'));
            }

            return callback(null, credentials);
        }

        // Entity: any

        return callback(null, credentials);
    });
Esempio n. 19
0
File: ticket.js Progetto: av4me/oz
exports.reissue = function (parentTicket, encryptionPassword, options, callback) {

    Hoek.toss(parentTicket, Boom.internal('Invalid parent ticket object'), callback);
    Hoek.toss(encryptionPassword, Boom.internal('Invalid encryption password'), callback);
    Hoek.toss(options, Boom.internal('Invalid options object'), callback);
    Hoek.toss(!options.scope || Scope.isSubset(parentTicket.scope, options.scope), Boom.forbidden('New scope is not a subset of the parent ticket scope'), callback);
    Hoek.toss(!options.issueTo || !parentTicket.dlg, Boom.badRequest('Cannot re-delegate'), callback);

    // Construct ticket

    var exp = (Hawk.utils.now() + (options.ttl || Settings.ticket.ttl));
    if (options.grantExp) {
        exp = Math.min(exp, options.grantExp);
    }

    var ticket = {
        exp: exp,
        app: options.issueTo || parentTicket.app,
        scope: options.scope || parentTicket.scope
    };

    if (options.ext || parentTicket.ext) {
        ticket.ext = options.ext || parentTicket.ext;
    }

    if (parentTicket.grant) {
        ticket.grant = parentTicket.grant;
        ticket.user = parentTicket.user;
    }

    if (options.issueTo) {
        ticket.dlg = parentTicket.app;
    }
    else if (parentTicket.dlg) {
        ticket.dlg = parentTicket.dlg;
    }

    exports.generate(ticket, encryptionPassword, callback);
};
Esempio n. 20
0
        function(localStorageService, $location, $window, $http, $scope) {
            const hawk = require('hawk');
            const host = $location.host();
            const port = $location.port();
            const loginUrl = `${$location.protocol()}://${host}:${port}/api/auth/login/`;
            const credentials = {
                id: $location.search().clientId,
                key: $location.search().accessToken,
                algorithm: 'sha256'
            };

            this.loginError = null;
            var payload = {
                credentials: credentials,
            };

            // Cribbed from taskcluster-tools. Save this for interacting with tc
            const results = $location.search();
            if (results.certificate) {
                results.certificate = JSON.parse(results.certificate);
                payload.ext = hawk.utils.base64urlEncode(JSON.stringify({"certificate": results.certificate}));
            }

            const header = hawk.client.header(loginUrl, 'GET', payload);

            // send a request from client side to TH server signed with TC
            // creds from login.taskcluster.net
            $http.get(loginUrl,
                      {headers: {"tcauth": header.field}})
                .then(function(resp) {
                    var user = resp.data;
                    user.loggedin = true;
                    localStorageService.set("user", user);
                    localStorageService.set('taskcluster.credentials', results);
                    $window.close();
                }, function(data) {
                    $scope.loginError = data.data.detail;
                });
        }
Esempio n. 21
0
    _useHawkAuth: function (request) {
        var authHeaderKey = "Authorization";
        var hawk_id = request.transformed.helperAttributes.hawk_id;
        var hawk_key = request.transformed.helperAttributes.hawk_key;
        var algorithm = request.transformed.helperAttributes.algorithm;
        var user = request.transformed.helperAttributes.user || undefined;
        var nonce = request.transformed.helperAttributes.nonce || Hawk.utils.randomString(6);
        var ext = request.transformed.helperAttributes.ext || undefined;
        var app = request.transformed.helperAttributes.app || undefined;
        var dlg = request.transformed.helperAttributes.dlg || undefined;
        var timestamp = request.transformed.helperAttributes.timestamp || undefined;

        var options = {
            credentials: {
                id: hawk_id,
                key: hawk_key,
                algorithm: algorithm
            },
            nonce: nonce,
            ext: ext,
            app: app,
            dlg: dlg,
            timestamp: timestamp,
            user: user
        };

        var result = Hawk.client.header(request.transformed.url, request.method, options);
        if (result.err) {
            Errors.requestError(request, new Error('Unable to compute Hawk Auth parameters: ' + result.err));
            return;
        }

        var headerObj = Helpers.generateHeaderObj(request.transformed.headers);
        headerObj[authHeaderKey] = result.field;
        request.transformed.headers = Helpers.generateHeaderStringFromObj(headerObj);
    },
Esempio n. 22
0
exports.credentialsFunc = function (encryptionPassword, options) {

    Hawk.utils.assert(encryptionPassword, 'Invalid encryption password');

    return function (id, callback) {

        // Parse ticket id

        Ticket.parse(id, encryptionPassword, options, function (err, ticket) {

            if (err) {
                return callback(err);
            }

            // Check expiration

            if (ticket.exp <= Hawk.utils.now()) {
                return callback(Hawk.utils.unauthorized('Expired ticket'));
            }

            return callback(null, ticket);
        });
    };
};
Esempio n. 23
0
File: ticket.js Progetto: rs22/oz
exports.issue = function (app, grant, encryptionPassword, options, callback) {

    const fail = Hoek.nextTick(callback);

    if (!app || !app.id) {
        return fail(Boom.internal('Invalid application object'));
    }

    if (grant && (!grant.id || !grant.user || !grant.exp)) {
        return fail(Boom.internal('Invalid grant object'));
    }

    if (!encryptionPassword) {
        return fail(Boom.internal('Invalid encryption password'));
    }

    if (!options) {
        return fail(Boom.internal('Invalid options object'));
    }

    const scope = options.scope || (grant && grant.scope) || app.scope || [];
    const error = Scope.validate(scope);
    if (error) {
        return fail(error);
    }

    if (grant &&
        grant.scope &&
        app.scope &&
        !Scope.isSubset(app.scope, grant.scope)) {

        return fail(Boom.internal('Grant scope is not a subset of the application scope'));
    }

    // Construct ticket

    let exp = (Hawk.utils.now() + (options.ttl || internals.defaults.ticketTTL));
    if (grant) {
        exp = Math.min(exp, grant.exp);
    }

    const ticket = {
        exp: exp,
        app: app.id,
        scope: scope
    };

    if (grant) {
        ticket.grant = grant.id;
        ticket.user = grant.user;
    }

    if (options.delegate === false) {           // Defaults to true
        ticket.delegate = false;
    }

    if (options.dlg) {
        ticket.dlg = options.dlg;
    }

    exports.generate(ticket, encryptionPassword, options, callback);
};
Esempio n. 24
0
File: endpoints.js Progetto: rs22/oz
        Server.authenticate(req, options.encryptionPassword, options, (err, ticket, artifacts) => {

            if (err) {
                return callback(err);
            }

            if (!ticket.user) {
                return callback(Hawk.utils.unauthorized('App ticket cannot be delegated'));
            }

            if (ticket.dlg) {
                return callback(Boom.badRequest('Cannot re-delegate'));
            }

            if (ticket.delegate === false) {          // Defaults to true
                return callback(Boom.forbidden('Ticket does not allow delegation'));
            }

            if (payload.scope) {
                error = Scope.validate(payload.scope);
                if (error) {
                    return callback(error);
                }

                if (!Scope.isSubset(ticket.scope, payload.scope)) {
                    return callback(Boom.forbidden('New scope is not a subset of the parent ticket scope'));
                }
            }

            // Load app

            options.loadAppFunc(ticket.app, (err, app) => {

                if (err) {
                    return callback(err);
                }

                if (!app) {
                    return callback(Hawk.utils.unauthorized('Invalid application'));
                }

                if (!app.delegate) {
                    return callback(Boom.forbidden('Application has no delegation rights'));
                }

                // Load delegated app

                options.loadAppFunc(payload.delegateTo, (err, delegatedApp) => {

                    if (err) {
                        return callback(err);
                    }

                    if (!delegatedApp) {
                        return callback(Hawk.utils.unauthorized('Invalid application'));
                    }

                    options.loadGrantFunc(ticket.grant, (err, grant, ext) => {

                        if (err) {
                            return callback(err);
                        }

                        if (!grant ||
                            grant.app !== ticket.app ||
                            grant.user !== ticket.user ||
                            !grant.exp ||
                            grant.exp <= Hawk.utils.now()) {

                            return callback(Hawk.utils.unauthorized('Invalid grant'));
                        }

                        return delegate(ticket, delegatedApp, grant);
                    });
                });
            });
        });
Esempio n. 25
0
File: ticket.js Progetto: rs22/oz
exports.reissue = function (parentTicket, grant, encryptionPassword, options, callback) {

    const fail = Hoek.nextTick(callback);

    if (!parentTicket) {
        return fail(Boom.internal('Invalid parent ticket object'));
    }

    if (!encryptionPassword) {
        return fail(Boom.internal('Invalid encryption password'));
    }

    if (!options) {
        return fail(Boom.internal('Invalid options object'));
    }

    if (parentTicket.scope) {
        const error = Scope.validate(parentTicket.scope);
        if (error) {
            return fail(error);
        }
    }

    if (options.scope) {
        const error = Scope.validate(options.scope);
        if (error) {
            return fail(error);
        }

        if (!Scope.isSubset(parentTicket.scope, options.scope)) {
            return fail(Boom.forbidden('New scope is not a subset of the parent ticket scope'));
        }
    }

    if (options.delegate &&
        parentTicket.delegate === false) {

        return fail(Boom.forbidden('Cannot override ticket delegate restriction'));
    }

    if (options.issueTo) {
        if (parentTicket.dlg) {
            return fail(Boom.badRequest('Cannot re-delegate'));
        }

        if (parentTicket.delegate === false) {          // Defaults to true
            return fail(Boom.forbidden('Ticket does not allow delegation'));
        }
    }

    if (grant && (!grant.id || !grant.user || !grant.exp)) {
        return fail(Boom.internal('Invalid grant object'));
    }

    if (grant || parentTicket.grant) {
        if (!grant ||
            !parentTicket.grant ||
            parentTicket.grant !== grant.id) {

            return fail(Boom.internal('Parent ticket grant does not match options.grant'));
        }
    }

    // Construct ticket

    let exp = (Hawk.utils.now() + (options.ttl || internals.defaults.ticketTTL));
    if (grant) {
        exp = Math.min(exp, grant.exp);
    }

    const ticket = {
        exp: exp,
        app: options.issueTo || parentTicket.app,
        scope: options.scope || parentTicket.scope
    };

    if (!options.ext &&
        parentTicket.ext) {

        options = Hoek.shallow(options);
        options.ext = parentTicket.ext;
    }

    if (grant) {
        ticket.grant = grant.id;
        ticket.user = grant.user;
    }

    if (options.issueTo) {
        ticket.dlg = parentTicket.app;
    }
    else if (parentTicket.dlg) {
        ticket.dlg = parentTicket.dlg;
    }

    if (options.delegate === false ||                   // Defaults to true
        parentTicket.delegate === false) {

        ticket.delegate = false;
    }

    exports.generate(ticket, encryptionPassword, options, callback);
};