Square API + iOS: OAuth не работает?

Я использую модуль OAuthSwift для реализации OAuth:

импортировать OAuthSwift

class OAuthViewController: UIViewController {

  let oauthswift = OAuth2Swift(
    consumerKey:    "sq0idp-kXxs-e8ARPIPWeYYLTwH_w",
    consumerSecret: "sq0csp-4KnbcED665wDZ_sDYIN8E9scnYiJGqm4xxdnYtgxSKM",
    authorizeUrl:   "https://connect.squareup.com/oauth2/authorize",
    accessTokenUrl: "https://connect.squareup.com/oauth2/token",
    responseType:   "code"
  )

  @IBAction func signInButtonTapped(_ sender: AnyObject) {
    oauthswift.authorize(
      withCallbackURL: "http://localhost:5000/edmund",
      scope: "MERCHANT_PROFILE_READ",
      state: "",
      success: { credential, response, parameters -> Void in
        print("success")
        print(credential)
      },
      failure: { error in
        print("error")
        print(error._code)
        print(error._domain)
        print(error.localizedDescription)
      }
    )
  }

}

Мой внутренний узел/экспресс-приложение просто перенаправляется, чтобы открыть мое приложение:

function edmund(req, resp) {
  console.log("resp");
  console.log(resp);
  resp.redirect(301, "indigoOMS:/");
}

Похоже, это работает:

введите описание изображения здесь

Однако в моем обработчике ошибок OAuth выводится ошибка: error -5 OAuthSwiftError Операция не может быть завершена. (Ошибка OAuthSwiftError -5.)

Вот что распечатывается в журнале моего узла:

resp
ServerResponse {
  domain: null,
  _events: { finish: [Function: resOnFinish] },
  _eventsCount: 1,
  _maxListeners: undefined,
  output: [],
  outputEncodings: [],
  outputCallbacks: [],
  outputSize: 0,
  writable: true,
  _last: false,
  upgrading: false,
  chunkedEncoding: false,
  shouldKeepAlive: true,
  useChunkedEncodingByDefault: true,
  sendDate: true,
  _removedHeader: {},
  _contentLength: null,
  _hasBody: true,
  _trailer: '',
  finished: false,
  _headerSent: false,
  socket:
   Socket {
     connecting: false,
     _hadError: false,
     _handle:
      TCP {
        bytesRead: 391,
        _externalStream: {},
        fd: 17,
        reading: true,
        owner: [Circular],
        onread: [Function: onread],
        onconnection: null,
        writeQueueSize: 0 },
     _parent: null,
     _host: null,
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: true,
     domain: null,
     _events:
      { end: [Object],
        finish: [Function: onSocketFinish],
        _socketEnd: [Function: onSocketEnd],
        drain: [Object],
        timeout: [Function],
        error: [Function: socketOnError],
        close: [Object],
        data: [Function: socketOnData],
        resume: [Function: onSocketResume],
        pause: [Function: onSocketPause] },
     _eventsCount: 10,
     _maxListeners: undefined,
     _writableState:
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: false,
        ended: false,
        finished: false,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: true,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: false,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     allowHalfOpen: true,
     destroyed: false,
     _bytesDispatched: 0,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server:
      Server {
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [Object],
        _usingSlaves: false,
        _slaves: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        _pendingResponseData: 0,
        _connectionKey: '6::::5000' },
     _server:
      Server {
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [Object],
        _usingSlaves: false,
        _slaves: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        _pendingResponseData: 0,
        _connectionKey: '6::::5000' },
     _idleTimeout: 120000,
     _idleNext:
      TimersList {
        _idleNext: [Circular],
        _idlePrev: [Circular],
        _timer: [Object],
        _unrefed: true,
        msecs: 120000 },
     _idlePrev:
      TimersList {
        _idleNext: [Circular],
        _idlePrev: [Circular],
        _timer: [Object],
        _unrefed: true,
        msecs: 120000 },
     _idleStart: 129529,
     parser:
      HTTPParser {
        '0': [Function: parserOnHeaders],
        '1': [Function: parserOnHeadersComplete],
        '2': [Function: parserOnBody],
        '3': [Function: parserOnMessageComplete],
        '4': [Function: onParserExecute],
        _headers: [],
        _url: '',
        _consumed: true,
        socket: [Circular],
        incoming: [Object],
        outgoing: null,
        maxHeaderPairs: 2000,
        onIncoming: [Function: parserOnIncoming] },
     on: [Function: socketOnWrap],
     _paused: false,
     read: [Function],
     _consuming: true,
     _httpMessage: [Circular] },
  connection:
   Socket {
     connecting: false,
     _hadError: false,
     _handle:
      TCP {
        bytesRead: 391,
        _externalStream: {},
        fd: 17,
        reading: true,
        owner: [Circular],
        onread: [Function: onread],
        onconnection: null,
        writeQueueSize: 0 },
     _parent: null,
     _host: null,
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: true,
     domain: null,
     _events:
      { end: [Object],
        finish: [Function: onSocketFinish],
        _socketEnd: [Function: onSocketEnd],
        drain: [Object],
        timeout: [Function],
        error: [Function: socketOnError],
        close: [Object],
        data: [Function: socketOnData],
        resume: [Function: onSocketResume],
        pause: [Function: onSocketPause] },
     _eventsCount: 10,
     _maxListeners: undefined,
     _writableState:
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: false,
        ended: false,
        finished: false,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: true,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: false,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     allowHalfOpen: true,
     destroyed: false,
     _bytesDispatched: 0,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server:
      Server {
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [Object],
        _usingSlaves: false,
        _slaves: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        _pendingResponseData: 0,
        _connectionKey: '6::::5000' },
     _server:
      Server {
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [Object],
        _usingSlaves: false,
        _slaves: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        _pendingResponseData: 0,
        _connectionKey: '6::::5000' },
     _idleTimeout: 120000,
     _idleNext:
      TimersList {
        _idleNext: [Circular],
        _idlePrev: [Circular],
        _timer: [Object],
        _unrefed: true,
        msecs: 120000 },
     _idlePrev:
      TimersList {
        _idleNext: [Circular],
        _idlePrev: [Circular],
        _timer: [Object],
        _unrefed: true,
        msecs: 120000 },
     _idleStart: 129529,
     parser:
      HTTPParser {
        '0': [Function: parserOnHeaders],
        '1': [Function: parserOnHeadersComplete],
        '2': [Function: parserOnBody],
        '3': [Function: parserOnMessageComplete],
        '4': [Function: onParserExecute],
        _headers: [],
        _url: '',
        _consumed: true,
        socket: [Circular],
        incoming: [Object],
        outgoing: null,
        maxHeaderPairs: 2000,
        onIncoming: [Function: parserOnIncoming] },
     on: [Function: socketOnWrap],
     _paused: false,
     read: [Function],
     _consuming: true,
     _httpMessage: [Circular] },
  _header: null,
  _headers: { 'x-powered-by': 'Express' },
  _headerNames: { 'x-powered-by': 'X-Powered-By' },
  _onPendingData: [Function: updateOutgoingData],
  req:
   IncomingMessage {
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: null,
        ended: true,
        endEmitted: false,
        reading: false,
        sync: true,
        needReadable: false,
        emittedReadable: true,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: true,
        decoder: null,
        encoding: null },
     readable: true,
     domain: null,
     _events: {},
     _eventsCount: 0,
     _maxListeners: undefined,
     socket:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: null,
        _readableState: [Object],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 10,
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: true,
        destroyed: false,
        _bytesDispatched: 0,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Object],
        _server: [Object],
        _idleTimeout: 120000,
        _idleNext: [Object],
        _idlePrev: [Object],
        _idleStart: 129529,
        parser: [Object],
        on: [Function: socketOnWrap],
        _paused: false,
        read: [Function],
        _consuming: true,
        _httpMessage: [Circular] },
     connection:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: null,
        _readableState: [Object],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 10,
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: true,
        destroyed: false,
        _bytesDispatched: 0,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Object],
        _server: [Object],
        _idleTimeout: 120000,
        _idleNext: [Object],
        _idlePrev: [Object],
        _idleStart: 129529,
        parser: [Object],
        on: [Function: socketOnWrap],
        _paused: false,
        read: [Function],
        _consuming: true,
        _httpMessage: [Circular] },
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers:
      { host: 'localhost:5000',
        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'accept-language': 'en-us',
        connection: 'keep-alive',
        'accept-encoding': 'gzip, deflate',
        'user-agent': 'Mozilla/5.0 (iPad; CPU OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E233 Safari/601.1' },
     rawHeaders:
      [ 'Host',
        'localhost:5000',
        'Accept',
        'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language',
        'en-us',
        'Connection',
        'keep-alive',
        'Accept-Encoding',
        'gzip, deflate',
        'User-Agent',
        'Mozilla/5.0 (iPad; CPU OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E233 Safari/601.1' ],
     trailers: {},
     rawTrailers: [],
     upgrade: false,
     url: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
     method: 'GET',
     statusCode: null,
     statusMessage: null,
     client:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: null,
        _readableState: [Object],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 10,
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: true,
        destroyed: false,
        _bytesDispatched: 0,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Object],
        _server: [Object],
        _idleTimeout: 120000,
        _idleNext: [Object],
        _idlePrev: [Object],
        _idleStart: 129529,
        parser: [Object],
        on: [Function: socketOnWrap],
        _paused: false,
        read: [Function],
        _consuming: true,
        _httpMessage: [Circular] },
     _consuming: false,
     _dumped: false,
     next: [Function: next],
     baseUrl: '',
     originalUrl: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
     _parsedUrl:
      Url {
        protocol: null,
        slashes: null,
        auth: null,
        host: null,
        port: null,
        hostname: null,
        hash: null,
        search: '?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        query: 'code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        pathname: '/edmund',
        path: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        href: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        _raw: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code' },
     params: {},
     query: { code: 'sq0cgp-eSEoyMl3mJYastHZvhucVg', response_type: 'code' },
     res: [Circular],
     body: {},
     _parsedOriginalUrl:
      Url {
        protocol: null,
        slashes: null,
        auth: null,
        host: null,
        port: null,
        hostname: null,
        hash: null,
        search: '?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        query: 'code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        pathname: '/edmund',
        path: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        href: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code',
        _raw: '/edmund?code=sq0cgp-eSEoyMl3mJYastHZvhucVg&response_type=code' },
     route: Route { path: '/edmund', stack: [Object], methods: [Object] } },
  locals: {} }

Что мне не хватает? Я чувствую, что делаю все правильно.


person bigpotato    schedule 27.10.2016    source источник
comment
Пара дополнительных вопросов, которые могут помочь: 1) Если вы регистрируете перенаправление, которое видит ваше приложение узла, работает ли токен доступа, который вы получаете? 2) Не уверен, как устроена эта библиотека, но нужно ли указывать accessTokenUrl? Это должно относиться только к responseType=code.   -  person Troy    schedule 28.10.2016
comment
@Troy собирается обновить вопрос в журнале, одну секунду   -  person bigpotato    schedule 28.10.2016
comment
Другим предложением было бы попытаться получить больше трассировки стека из вашей ошибки (извините, у меня нет опыта отладки Swift, но должен быть способ сделать это).   -  person Troy    schedule 28.10.2016
comment
@Troy, я обновил вопрос с помощью журнала узлов. К сожалению, объект ошибки не выглядит так, как будто у него есть другие методы. Тем не менее, я просмотрел -5, который распечатывает OAuthSwiftError, и нашел это: ">github.com/OAuthSwift/OAuthSwift/blob/   -  person bigpotato    schedule 28.10.2016
comment
оооо, я распечатал описание и получил serverError[No access_token, no code and no error provided by server]... хотя не уверен, что это имеет какой-то смысл...   -  person bigpotato    schedule 28.10.2016
comment
Извините, я понял, в чем дело, сразу после того, как опубликовал свой последующий комментарий.   -  person Troy    schedule 28.10.2016


Ответы (2)


Я думаю, что вы не можете передать какие-либо параметры в свое приложение, потому что ваше перенаправление 301 удаляет их из URI. Может быть, что-то вроде этого?

var URL = require('url');
...

function edmund(req, resp) {
  console.log("resp");
  console.log(resp);
  var queryString = URL.parse(req.url).query;
  resp.redirect(301, "indigoOMS:/?" + queryString);
}
person Troy    schedule 27.10.2016
comment
сейчас попробую, секундочку! - person bigpotato; 28.10.2016
comment
является ли uri пакетом npm? - person bigpotato; 28.10.2016
comment
Да, я думаю, что это должно быть встроено. Вы также можете просто разделить URL-адрес запроса на '?', но это менее безопасно. РЕДАКТИРОВАТЬ: К сожалению, я манекен, это URL. Давно не писал node. - person Troy; 28.10.2016
comment
хорошо, мне нужно идти, но я поработаю над этим завтра, обязательно буду держать вас в курсе. спасибо за помощь Трой! это выглядит многообещающе!! - person bigpotato; 28.10.2016
comment
оно работает! я использовал: let url_parts = URL.parse(req.url, true); и url_parts.query - person bigpotato; 28.10.2016

URL-адрес перенаправления должен быть веб-страницей, размещенной на сервере, вам не повезет, если вы попытаетесь перенаправить на мобильное приложение. Если вы создаете приложение для личного использования, вместо этого вы можете использовать свой личный токен доступа. Какое приложение вы создаете?

person tristansokol    schedule 27.10.2016
comment
Теоретически это должно работать, поскольку OP настроил небольшой сервер на локальном хосте для получения запроса (обратите внимание, что localhost является явно разрешенным исключением из правила только https). - person Troy; 28.10.2016
comment
да, как упомянул @Troy, это работает, так как я настроил localhost. сервер получает запрос от Square, и мое приложение в конечном итоге открывается, но OAuthSwift все еще печатает error - person bigpotato; 28.10.2016
comment
Да, но если вы просто собираетесь использовать API на локальных компьютерах, гораздо проще использовать ваш личный токен доступа вместо использования OAuth. И если вы планируете использовать API на машинах, отличных от вашей собственной, то подход с локальным сервером не сработает. - person tristansokol; 28.10.2016