jlguenego / node-expose-sspi

Expose Microsoft Windows SSPI to Node for SSO authentication.
ISC License
128 stars 19 forks source link

(error code: 0x80090308) on session timeouts w/ multiple requests #116

Open anotherCoward opened 3 years ago

anotherCoward commented 3 years ago

This is related to #91 and if the proxy is not set to keep alive, multiple requests in a short duration will all fail to authenticate with the message Error: AcceptSecurityContext: SECURITY_STATUS incorrect (<0): (error code: 0x80090308) [invalid token message] because a server context handle could not be found.

If I request now multiple files (after the timeout of the session) using something like:

for (let i = 0; i < 10;i++) { 
  fetch(`/uri?file=${i}`).then(whatever);
}

all requests will fail. But if i request one first and then the rest (during the session period) everything is fine.

From what I can tell the handle get's added and removed all over again using the configuration values mentioned in #91. I suggest changing the handle name/detection to something different instead of a handle based on the client IP, if possible. There are enough values in the header block that could help creating a more unique handle to search for.

On the other side, if useSession is enabled you could store the serverContextHandle tempoarily inside the session instead of the ServerContextHandleManager.

I have also implemented locally a small addition inside the catch block at auth.js for two of the error codes related to SECURITY_STATUS in cases where the session timed out it triggers 0x80090308 or 0x80090310. If so i just ask the client to auth again using a forward request with Status 308. So fetch() or XMLHttpRequest() create a new session again without any problem. (But only one request for a given IP at a time... :/)

  if (opts.useSession && (e.message.includes("(error code: 0x80090310)") || e.message.includes("(error code: 0x80090308)"))) {
    schManager.release(req);
    delete req.session.sso;
    debug("requesting auth again");
    res.status(308);
    res.set('Location', req.url);
    res.end();
  } else { /* default */ }

But I'm not sure if this fits for everyone. Header Status 308 tells the client also to resend forward form data and so on.

anotherCoward commented 3 years ago

Today i tried it by adding the Forwarded header in the nginx config using: proxy_set_header Forwarded "for=\"$remote_addr:$remote_port\"";

Using this it seems to work fine on multiple requests at once after the session timed out.

Just dumps of schManager.cache on set/release/get:

Before "Forwarded":

set Map(0) {}
release Map(1) {
  '10.81.234.6_close' => {
    handle: '****',
    timestamp: 1636655728379
  }
}
release Map(0) {}
get Map(0) {}
Error: AcceptSecurityContext: SECURITY_STATUS incorrect (<0): (error code: 0x80090308)
release Map(0) {}
set Map(0) {}
Map(1) {
  '10.81.234.6_close' => {
    handle: '****',
    timestamp: 1636655868881
  }
}
[...] 

After:

release Map(0) {}
get Map(0) {}
set Map(1) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  }
}
get Map(1) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  }
}
release Map(1) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  }
}
get Map(1) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  }
}
set Map(2) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  },
  'for="10.81.234.7:65464"' => {
    handle: '****',
    timestamp: 1636701143666
  }
}
release Map(2) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  },
  'for="10.81.234.7:65464"' => {
    handle: '****',
    timestamp: 1636701143666
  }
}
get Map(2) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  },
  'for="10.81.234.7:65464"' => {
    handle: '****',
    timestamp: 1636701143666
  }
}
set Map(3) {
  'for="10.81.234.7:65462"' => {
    handle: '****',
    timestamp: 1636701143570
  },
  'for="10.81.234.7:65464"' => {
    handle: '****',
    timestamp: 1636701143666
  },
  'for="10.81.234.7:65468"' => {
    handle: '****',
    timestamp: 1636701143694
  }
}