ZenDesk App OAuth-аутентификация на основе браузера

Я работаю над приложением ZenDesk, которое извлекает информацию о клиентах из серверной системы. Нам нужно пройти аутентификацию в этой системе, используя поток аутентификации OAuth 2 на основе браузера.

Это не проблема, чтобы включить ссылку на страницу аутентификации, что-то вроде:

https://oauth2server.com/auth?
    response_type=token&
    client_id=CLIENT_ID&
    redirect_uri=REDIRECT_URI&
    scope=photos

Однако, как только пользователь вошел в систему, сервер OAuth хочет перенаправить клиента и включить токен авторизации. Таким образом, REDIRECT_URI обычно будет выглядеть так:

https://example.zendesk.com/agent/#token=ACCESS_TOKEN

Однако ZenDesk уже использует идентификатор фрагмента, чтобы указать, какой контент показывать на странице:

https://example.zendesk.com/agent/#/dashboard
https://example.zendesk.com/agent/#/tickets/1234

Мое приложение ZD отображается только на определенных страницах, поэтому как мне

  • запустить приложение и запустить Javascript, и
  • есть идентификатор фрагмента с токеном авторизации?

(У меня есть контроль над внутренним сервером OAuth, поэтому, если вы не можете придумать хороший чистый способ сделать это, предложения по взлому на стороне сервера OAuth также будут с благодарностью приняты.)

2 ответа

Решение

Вот действительно простое приложение ZenDesk (версия фреймворка 0.5), которое

  1. аутентифицируется против Google (в отдельном всплывающем окне)
  2. извлекает пользовательское значение поля заявки из видимой в данный момент заявки
  3. получает имя пользователя Google

В manifest.json это приложение ZenDesk должно указывать "location": "ticket_sidebar",

app.js

(function (window) {
    return {
        zenDeskSubdomain: 'YOUR_ZENDESK_SUBDOMAIN',
        googleClientId: 'YOUR_GOOGLE_CLIENT_ID',

        events: {
            'app.activated': 'onActivate',
            'app.deactivated': 'onDeactivate',
            'click .loginout': 'onLogInOutClick',
            'click .show-username': 'onShowUserNameClick'
        },

        requests: {
            getUserInfo: function (access_token) {
                return {
                    url: 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + access_token,
                    type: 'GET',
                    proxy_v2: true
                };
            }
        },

        onActivate: function () {
            console.info("onActivate()");
            this.accessToken();
            var user_id = this.ticket().customField("custom_field_22931898");
            this.$('.userid').text(user_id);
        },

        onDeactivate: function () {
            console.info("onDeactivate()");
            if (this.timer) {
                clearTimeout(this.timer);
            }
        },

        onShowUserNameClick: function () {
            var access_token = this.accessToken();

            if (!access_token) {
                console.info("Can't do it!  No access_token!");
                return;
            }

            this.ajax('getUserInfo', access_token)
                .done(function (data) {
                    console.info(data);
                    this.$('.username').text(data.name);
                });
        },

        onLogInOutClick: function (event) {
            if (this.accessToken()) {
                this.logout(event);
            } else {
                this.login(event);
            }
        },

        login: function (event) {
            console.info("login()");
            event.preventDefault();
            var popup = this.createLoginPopup();
            this.awaitAccessToken(popup);
        },

        logout: function (event) {
            console.info("logout()");
            event.preventDefault();
            this.accessToken(null);
            console.info("  access_token = " + this.accessToken());
            this.$('.loginout').text('login');
        },

        createLoginPopup: function () {
            console.info("createLoginPopup()");
            return window.open(
            'https://accounts.google.com/o/oauth2/auth?response_type=token&client_id=' + this.googleClientId + '&redirect_uri=https%3A%2F%2F' + this.zenDeskSubdomain + '.zendesk.com%2Fagent%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile',
            'Login Popup',
            'width=400,height=400');
        },

        timer: null,
        awaitAccessToken: function (popup) {
            console.info("awaitAccessToken()");
            if (this.isLoaded(popup)) {
                console.info("  popup is loaded");
            } else {
                console.info("  popup is NOT loaded; sleeping");
                var t = this;
                this.timer = setTimeout(
                    function () { t.awaitAccessToken(popup); },
                    1000);
                return;
            }

            var access_token = this.parseAccessToken(popup.location.href);

            if (access_token) {
                console.info('  access_token = ' + access_token);
                popup.close();
                this.accessToken(access_token);
            } else {
                services.notify('Error requesting code...');
            }
        },

        isLoaded: function (win) {
            try {
                return ('about:blank' !== win.location.href)
                    && (null !== win.document.body.innerHTML);
            } catch (err) {
                return false;
            }
        },

        parseAccessToken: function (uri) {
            var match = uri.match(/[#&]access_token=([^&]*)/i);
            return match[1] || null;
        },

        accessToken: function (value) {
            if (1 === arguments.length) {
                console.info("Storing access_token = " + value);
                this.store({ access_token: value });
            }

            var token = this.store('access_token');
            console.info("access_token = " + value);

            var loginout = this.$('.loginout');
            if (token) {
                loginout.text('logout');
            } else {
                loginout.text('login');
            }

            return token;
        }
    };
}(this));

layout.hdbs

<header>
  <span class="logo"/>
  <h3>{{setting "name"}}</h3>
</header>
<section data-main/>
<footer>
    <div><a class="loginout">login</a></div>
    <div><a class="show-username">show username</a></div>
    <div><b>user id: </b><span class="userid">unknown</span></div>
    <div><b>username: </b><span class="username">unknown</span></div>
</footer>

Конфигурация Google OAuth

Настройте Google OAuth, чтобы разрешить трафик из вашего приложения.

Перенаправить URI Происхождение Javascript

Вы можете использовать легкое серверное приложение для управления потоком авторизации и токенами. Приложение Zendesk может связываться с ним через помощник iframe фреймворка. Я написал расширенное руководство для Справочного центра Zendesk по адресу https://support.zendesk.com/hc/en-us/articles/205225558.

Другие вопросы по тегам