GetStream / stream-js

JS / Browser Client - Build Activity Feeds & Streams with GetStream.io
https://getstream.io
BSD 3-Clause "New" or "Revised" License
329 stars 109 forks source link

GetStream is incompatible with auth0 social login userIds #153

Closed AndreasGalster closed 6 years ago

AndreasGalster commented 6 years ago

When using auth0, you usually get user IDs such as this:

google-oauth2|12341123123

First, the regex in the utils lib does only support user IDs with characters and dots. Changing from this regex

var validRe = /^[\w-]+$/;

to this fixes that issue.

var validRe = /^[\w-\|]+$/;

However looks like stream-js still doesn't allow those kind of userIds, looks like there is another validation at some point in your backend. Is it possible to allow the | character in userIds?

Maybe this error log is helpful:

{ Error: {"detail":"","status_code":404,"code":16,"exception":"DoesNotExistException","duration":"0.00ms"} with HTTP status code 404
    at StreamApiError.ErrorAbstract (/mnt/c/netly/productnews-graphql/node_modules/getstream/src/lib/errors.js:20:11)
    at new StreamApiError (/mnt/c/netly/productnews-graphql/node_modules/getstream/src/lib/errors.js:87:17)
    at Request.task [as _callback] (/mnt/c/netly/productnews-graphql/node_modules/getstream/src/lib/client.js:161:16)
    at Request.self.callback (/mnt/c/netly/productnews-graphql/node_modules/getstream/node_modules/request/request.js:187:22)
    at Request.emit (events.js:180:13)
    at Request.emit (domain.js:421:20)
    at Request.<anonymous> (/mnt/c/netly/productnews-graphql/node_modules/getstream/node_modules/request/request.js:1048:10)
    at Request.emit (events.js:180:13)
    at Request.emit (domain.js:421:20)
    at IncomingMessage.<anonymous> (/mnt/c/netly/productnews-graphql/node_modules/getstream/node_modules/request/request.js:969:12)
    at IncomingMessage.emit (events.js:185:15)
    at IncomingMessage.emit (domain.js:421:20)
    at endReadableNT (_stream_readable.js:1101:12)
    at process._tickCallback (internal/process/next_tick.js:114:19)
  error:
   { detail: '',
     status_code: 404,
     code: 16,
     exception: 'DoesNotExistException',
     duration: '0.00ms' },
  response:
   IncomingMessage {
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [BufferList],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: true,
        endEmitted: true,
        reading: false,
        sync: true,
        needReadable: false,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        destroyed: false,
        defaultEncoding: 'utf8',
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: false,
     domain: null,
     _events:
      { end: [Array],
        close: [Array],
        data: [Function],
        error: [Function] },
     _eventsCount: 4,
     _maxListeners: undefined,
     socket:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: 'api.stream-io-api.com',
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 8,
        connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'api.stream-io-api.com',
        _readableState: [ReadableState],
        readable: false,
        domain: null,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _bytesDispatched: 921,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [ClientRequest],
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1,
        [Symbol(res)]: [TLSWrap],
        [Symbol(asyncId)]: 8791,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(bytesRead)]: 429,
        [Symbol(connect-options)]: [Object] },
     connection:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: 'api.stream-io-api.com',
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 8,
        connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'api.stream-io-api.com',
        _readableState: [ReadableState],
        readable: false,
        domain: null,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _bytesDispatched: 921,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [ClientRequest],
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1,
        [Symbol(res)]: [TLSWrap],
        [Symbol(asyncId)]: 8791,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(bytesRead)]: 429,
        [Symbol(connect-options)]: [Object] },
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers:
      { 'content-type': 'text/plain; charset=utf-8',
        'content-length': '97',
        connection: 'close',
        date: 'Wed, 21 Mar 2018 11:49:30 GMT',
        server: 'nginx',
        'x-cache': 'Error from cloudfront',
        via: '1.1 ee6ddcd79938825b919ad3220f210cff.cloudfront.net (CloudFront)',
        'x-amz-cf-id': 'FLjosqJw0vT8slOA5qRJNUKGUyKBsNDdbaNpuj39SNbzubP3L0bgDA==' },
     rawHeaders:
      [ 'Content-Type',
        'text/plain; charset=utf-8',
        'Content-Length',
        '97',
        'Connection',
        'close',
        'Date',
        'Wed, 21 Mar 2018 11:49:30 GMT',
        'Server',
        'nginx',
        'X-Cache',
        'Error from cloudfront',
        'Via',
        '1.1 ee6ddcd79938825b919ad3220f210cff.cloudfront.net (CloudFront)',
        'X-Amz-Cf-Id',
        'FLjosqJw0vT8slOA5qRJNUKGUyKBsNDdbaNpuj39SNbzubP3L0bgDA==' ],
     trailers: {},
     rawTrailers: [],
     upgrade: false,
     url: '',
     method: null,
     statusCode: 404,
     statusMessage: 'Not Found',
     client:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: 'api.stream-io-api.com',
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 8,
        connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'api.stream-io-api.com',
        _readableState: [ReadableState],
        readable: false,
        domain: null,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _bytesDispatched: 921,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [ClientRequest],
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1,
        [Symbol(res)]: [TLSWrap],
        [Symbol(asyncId)]: 8791,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(bytesRead)]: 429,
        [Symbol(connect-options)]: [Object] },
     _consuming: true,
     _dumped: false,
     req:
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedConnection: false,
        _removedContLen: false,
        _removedTE: false,
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [TLSSocket],
        connection: [TLSSocket],
        _header: 'POST /api/v1.0/feed/user/google-oauth2%7C106913991226465822832/?api_key=7t3whdup6txn&location=unspecified HTTP/1.1\r\nstream-auth-type: simple\r\nAuthorization: usergoogle-oauth2|106913991226465822832 EKmXPg1fgZ8n2RZbM9TL_IB910Y\r\nX-Stream-Client: stream-javascript-client-node-unknown\r\nhost: api.stream-io-api.com\r\naccept: application/json\r\ncontent-type: application/json\r\ncontent-length: 510\r\nConnection: close\r\n\r\n',
        _onPendingData: [Function: noopPendingOutput],
        agent: [Agent],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/api/v1.0/feed/user/google-oauth2%7C106913991226465822832/?api_key=7t3whdup6txn&location=unspecified',
        _ended: true,
        res: [Circular],
        aborted: undefined,
        timeoutCb: null,
        upgradeOrConnect: false,
        parser: null,
        maxHeadersCount: null,
        [Symbol(isCorked)]: false,
        [Symbol(outHeadersKey)]: [Object] },
     request:
      Request {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        body: '{"actor":"user:google-oauth2|106913991226465822832","verb":"tweet","object":"story:{ votesCount: 1,\\n  commentsCount: 0,\\n  voters: [],\\n  _id: 5ab246c7cdafdd51c5facb45,\\n  authorId: \'google-oauth2|106913991226465822832\',\\n  comment: \'asdfasdfsadfasdfasd\',\\n  date: 2018-03-21T11:49:27.812Z,\\n  __v: 0 }","foreign_id":"comment:5ab246c7cdafdd51c5facb45","time":"2018-03-21T11:49:27.812Z","tweet":"asdfasdfsadfasdfasd","description":"asdfasdfsadfasdfasd","to":["timeline:cpaentries B1Vj9i6huAPNwcXRiBvuZtD7P8E"]}',
        signature: 'usergoogle-oauth2|106913991226465822832 EKmXPg1fgZ8n2RZbM9TL_IB910Y',
        headers: [Object],
        withCredentials: false,
        method: 'POST',
        callback: [Function],
        readable: true,
        writable: true,
        explicitMethod: true,
        _qs: [Querystring],
        _auth: [Auth],
        _oauth: [OAuth],
        _multipart: [Multipart],
        _redirect: [Redirect],
        _tunnel: [Tunnel],
        setHeader: [Function],
        hasHeader: [Function],
        getHeader: [Function],
        removeHeader: [Function],
        localAddress: undefined,
        pool: {},
        dests: [],
        __isRequestRequest: true,
        _callback: [Function: task],
        uri: [Url],
        proxy: null,
        tunnel: true,
        setHost: true,
        originalCookieHeader: undefined,
        _disableCookies: true,
        _jar: undefined,
        port: 443,
        host: 'api.stream-io-api.com',
        url: [Url],
        path: '/api/v1.0/feed/user/google-oauth2%7C106913991226465822832/?api_key=7t3whdup6txn&location=unspecified',
        _json: true,
        httpModule: [Object],
        agentClass: [Function],
        agent: [Agent],
        _started: true,
        href: 'https://api.stream-io-api.com/api/v1.0/feed/user/google-oauth2%7C106913991226465822832/?api_key=7t3whdup6txn&location=unspecified',
        req: [ClientRequest],
        ntick: true,
        response: [Circular],
        originalHost: 'api.stream-io-api.com',
        originalHostHeaderName: 'host',
        responseContent: [Circular],
        _destdata: true,
        _ended: true,
        _callbackCalled: true },
     toJSON: [Function: responseToJSON],
     caseless: Caseless { dict: [Object] },
     read: [Function],
     body:
      { detail: '',
        status_code: 404,
        code: 16,
        exception: 'DoesNotExistException',
        duration: '0.00ms' } },
  message: '{"detail":"","status_code":404,"code":16,"exception":"DoesNotExistException","duration":"0.00ms"} with HTTP status code 404' }
dwightgunning commented 6 years ago

Thanks for the report; Auth0 is great!

I've asked our API team to see whether we can allow the vertical bar / pipe character. I'll let you know what I learn.

Side-note: the validRe regex was updated recently relation to a couple issues with resolved in the latest release. Doesn't change your issue report but thought I'd mention in case you wanted to pull the latest.

AndreasGalster commented 6 years ago

Thanks a lot, hoping for positive feedback because it's a major blocker for us to migrate to stream :( :)

dwightgunning commented 6 years ago

Update from the product team that this change will go onto the roadmap but it isn't possible to prioritise it right away.

In the meantime it should be fairly straight forward to implement a pair of simple mapping functions that convert to/from '|' used by Auth0 into another supported character(s) for use in Stream's Feed Ids.

AndreasGalster commented 6 years ago

Yea I see, ok I guess we'll go with this for now. Thanks for the info, hoping to see this in the product soon 😄.

I wonder though if it's actually possible to edit existing stream activities? It looks like it's only possible to view on the dashboard (maybe via API?). E. g. if we want to change all the userIds retrospectively in the activities and get rid of the mapping.

(This is just one use case, I have many use cases where I might want to edit activities retrospectively)

dwightgunning commented 6 years ago

Updates are certainly possible: https://getstream.io/docs/#activity-update

I'll close this ticket for now.