Open DerekMaggio opened 6 years ago
const api = require('@ucd-lib/fin-node-api');
const jwt = require('jsonwebtoken')
// I ran this command to create a user
// docker-compose exec basic-auth node service/cli create-user -u csus -p password -e s.project.csus@gmail.com
values = {
username: 'csus',
password: 'password',
admin: 'false'
}
//This is the line from the .env file
// JWT_SECRET=lax
token = jwt.sign(values, 'lax')
config_vals = {
host:'http://localhost:3000',
jwt: token,
username: 'csus',
password: 'password'
}
api.setConfig(config_vals)
console.log(api.getConfig())
@jrmerz is this on the right track to setup the fin-node-api config?
@jrmerz just throwing another mention so you see the above comment.
Close, here is the server function that mints jwt tokens: https://github.com/UCDavisLibrary/fin-server/blob/master/node-utils/jwt.js#L51. Note the payload is just username and admin flag. Then you pass in the secret as well as additional TTL (time to live) and issuer values. The TTL and issuer are used to expire the token as well as verify who issued token.
And here are the params required for fin-node-api config: https://github.com/UCDavisLibrary/fin-server/blob/master/server/index.js#L20. basePath is almost always '/fcrepo/rest' and can be ignored. Host is required. Jwt is required. The api library will then be able to make requests as the minted JWT username until the TTL expires, then you need to mint a new JWT. The system does support refresh tokens, but I would ignore those for now.
Here are some quick samples. Note, path does NOT include /fcrepo/rest, this is added for you:
HEAD request:
let response = await api.head({path});
is this response path a rdf or binary container?
if( api.isRdfContainer(response) ) {
// is rdf
}
get rdf as jsonld, check response status code
let response = await api.get({
path,
headers : {
accept : api.RDF_FORMATS.JSON_LD
}
});
if( !response.checkStatus(200) ) {
// badness
}
// grab and parse json. the last property is the http response object from the last
// http request made, some calls from the library make multiple http requests.
let jsonld = JSON.parse(response.last.body);
Perfect thanks man! This is working so far. I really appreciate it!!!
const api = require('@ucd-lib/fin-node-api');
const jwt = require('jsonwebtoken')
const config = require('../config')
/*
Not sure if this file is going to stay or not.
I just want to get this process tracked by git before I forget how to do it.
Created user 'csus' and set the as non-admin
What is going to be done is the values JSON is going to be hashed with a secret that will be stored on
our server and the UC Davis local instance. The result of this has is a JSON Web Token (jwt)
This how we are going to provide user authentication.
*/
values = {
username: 'csus',
admin: 'false'
}
// Want the token to be good for 7 weeks so that way I don't have to worry about reissuing it.
// We are specifying the issuer for fin to validate against.
options = {
expiresIn: '7w',
issuer: config.jwt_issuer
}
token = jwt.sign(values, config.jwt_secret, options)
/*
The required values to configure fin-node-api are host, basePath, and the jwt.
Definitions:
host: where the server is located at
basePath: the path to the fin repo. (defaulted to /fcrepo/rest)
jwt: your created JSON Web Token
*/
config_vals = {
host:'http://localhost:3000',
jwt: token,
}
api.setConfig(config_vals)
// Just specifying a path that I know exists
// path = 'collection/example_3-catalogs'
path = ''
let response = api.get({path});
// Returns a promise. Have to wait for the promise to resolve to get the value
response.then(function (result) {
console.log(result)
})
@jrmerz I am trying to access the root of the LDP to see what different DAMS I have access to. For instance, users, and catalogs. Even though I am sending a validated token along I am getting a 403 error. What I do not understand is that I am able to access collection/example_3-catalogs.
The main reason that I am do this is I am trying to set up connecting users to the queries that they have sent. Am I going about doing this incorrectly?
Does csus
have access to the root of the DAMS? by default the root is not publicly readable. You need to either give csus
access to root or mint the token with admin=true.
Note, booleans are not strings:
values = {
username: 'csus',
admin: false
}
Pro tip, you can directly link to git branchs/commits via urls so you don't have to paste the entire files code: https://github.com/ucd-library/csus-sp-2018-app/blob/f20a957bf52aa7b58ac65b1f3af185d13a6520c3/middlewares/fin_communication_wrapper.js
You can even link to a line in a file (just click the line number): https://github.com/ucd-library/csus-sp-2018-app/blob/f20a957bf52aa7b58ac65b1f3af185d13a6520c3/middlewares/fin_communication_wrapper.js#L19
raw url looks like:
https://github.com/ucd-library/csus-sp-2018-app/blob/f20a957bf52aa7b58ac65b1f3af185d13a6520c3/middlewares/fin_communication_wrapper.js#L19
@jrmerz I attempted your suggestion and I am still getting a 403 error. I am pretty stuck right now and need to get moving on sending our data to LDP in order to get current on our sprints.
Can you send over the full request you are making? Include headers so I can see the JWT token in use.
I am not actually using request to make the call. I am using fin-node-api. Do you want me to send to you the parameters I am passing to the api.get call?
Please. Include the JWT token as well
This is code I am using to mint my token and send the call. I left the requires out to condense down the message
values = {
username: 'csus',
admin: 'true'
}
options = {
expiresIn: '7w',
issuer: 'mylibrary.org'
}
let secret = 'lax';
token = jwt.sign(values, secret, options)
config_vals = {
host:'http://localhost:3000',
jwt: token,
}
api.setConfig(config_vals)
path = ''
let response = api.get({path});
response.then(function (result) {
console.log(result)
})
Here is the response that I am getting
ApiResponse {
httpStack:
[ IncomingMessage {
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Object],
connection: [Object],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: 'Forbidden',
client: [Object],
_consuming: true,
_dumped: false,
req: [Object],
request: [Object],
toJSON: [Function: responseToJSON],
caseless: [Object],
read: [Function],
body: '<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>\n<title>Error 403 Forbidden</title>\n</head>\n<body><h2>HTTP ERROR 403</h2>\n<p>Problem accessing /fcrepo/rest. Reason:\n<pre> Forbidden</pre></p><hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.6.v20170531</a><hr/>\n\n</body>\n</html>\n',
finAuthenticated: true } ],
last:
IncomingMessage {
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: [Object],
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:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
connection:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers:
{ 'x-powered-by': 'Express',
connection: 'close',
'cache-control': 'no-cache, no-store, must-revalidate',
'content-type': 'text/html;charset=iso-8859-1',
'content-length': '327',
server: 'Jetty(9.4.6.v20170531)',
expires: '0',
pragma: 'no-cache',
'set-cookie': [Array],
date: 'Wed, 07 Nov 2018 17:54:57 GMT' },
rawHeaders:
[ 'X-Powered-By',
'Express',
'connection',
'close',
'Cache-Control',
'no-cache, no-store, must-revalidate',
'content-type',
'text/html;charset=iso-8859-1',
'content-length',
'327',
'server',
'Jetty(9.4.6.v20170531)',
'Expires',
'0',
'Pragma',
'no-cache',
'set-cookie',
'fin-sid=s%3A1fPXzrwaDK9YoQwFqKaACkWGlVE6F-Yj.b8MCB5fU64WTdJF%2BBHwQMg9SI4Cgd1%2B66Yj%2BN0%2BjfGw; Path=/; Expires=Wed, 14 Nov 2018 17:54:57 GMT; HttpOnly',
'Date',
'Wed, 07 Nov 2018 17:54:57 GMT' ],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: 'Forbidden',
client:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
_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: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'GET /fcrepo/rest HTTP/1.1\r\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNzdXMiLCJhZG1pbiI6InRydWUiLCJpYXQiOjE1NDE2MTMyOTcsImV4cCI6MTU0NTg0Njg5NywiaXNzIjoibXlsaWJyYXJ5Lm9yZyJ9.8jwSif9fqL7FuFJppI-um_sUZYQ_m8uVwqDsG5GhG68\r\nCache-Control: no-cache\r\nUser-Agent: fin-node-api\r\nhost: localhost:3000\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/fcrepo/rest',
_ended: true,
res: [Circular],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
[Symbol(outHeadersKey)]: [Object] },
request:
Request {
domain: null,
_events: [Object],
_eventsCount: 5,
_maxListeners: undefined,
method: 'GET',
headers: [Object],
uri: [Object],
callback: [Function],
readable: true,
writable: true,
explicitMethod: true,
_qs: [Object],
_auth: [Object],
_oauth: [Object],
_multipart: [Object],
_redirect: [Object],
_tunnel: [Object],
setHeader: [Function],
hasHeader: [Function],
getHeader: [Function],
removeHeader: [Function],
localAddress: undefined,
pool: {},
dests: [],
__isRequestRequest: true,
_callback: [Function],
proxy: null,
tunnel: false,
setHost: true,
originalCookieHeader: undefined,
_disableCookies: true,
_jar: undefined,
port: '3000',
host: 'localhost',
path: '/fcrepo/rest',
httpModule: [Object],
agentClass: [Object],
agent: [Object],
_started: true,
href: 'http://localhost:3000/fcrepo/rest',
req: [Object],
ntick: true,
response: [Circular],
originalHost: 'localhost:3000',
originalHostHeaderName: 'host',
responseContent: [Circular],
_destdata: true,
_ended: true,
_callbackCalled: true },
toJSON: [Function: responseToJSON],
caseless: Caseless { dict: [Object] },
read: [Function],
body: '<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>\n<title>Error 403 Forbidden</title>\n</head>\n<body><h2>HTTP ERROR 403</h2>\n<p>Problem accessing /fcrepo/rest. Reason:\n<pre> Forbidden</pre></p><hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.6.v20170531</a><hr/>\n\n</body>\n</html>\n',
finAuthenticated: true },
data:
IncomingMessage {
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: [Object],
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:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
connection:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers:
{ 'x-powered-by': 'Express',
connection: 'close',
'cache-control': 'no-cache, no-store, must-revalidate',
'content-type': 'text/html;charset=iso-8859-1',
'content-length': '327',
server: 'Jetty(9.4.6.v20170531)',
expires: '0',
pragma: 'no-cache',
'set-cookie': [Array],
date: 'Wed, 07 Nov 2018 17:54:57 GMT' },
rawHeaders:
[ 'X-Powered-By',
'Express',
'connection',
'close',
'Cache-Control',
'no-cache, no-store, must-revalidate',
'content-type',
'text/html;charset=iso-8859-1',
'content-length',
'327',
'server',
'Jetty(9.4.6.v20170531)',
'Expires',
'0',
'Pragma',
'no-cache',
'set-cookie',
'fin-sid=s%3A1fPXzrwaDK9YoQwFqKaACkWGlVE6F-Yj.b8MCB5fU64WTdJF%2BBHwQMg9SI4Cgd1%2B66Yj%2BN0%2BjfGw; Path=/; Expires=Wed, 14 Nov 2018 17:54:57 GMT; HttpOnly',
'Date',
'Wed, 07 Nov 2018 17:54:57 GMT' ],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: 'Forbidden',
client:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: 'localhost',
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 346,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Object],
read: [Function],
_consuming: true,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 8,
[Symbol(bytesRead)]: 778 },
_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: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'GET /fcrepo/rest HTTP/1.1\r\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNzdXMiLCJhZG1pbiI6InRydWUiLCJpYXQiOjE1NDE2MTMyOTcsImV4cCI6MTU0NTg0Njg5NywiaXNzIjoibXlsaWJyYXJ5Lm9yZyJ9.8jwSif9fqL7FuFJppI-um_sUZYQ_m8uVwqDsG5GhG68\r\nCache-Control: no-cache\r\nUser-Agent: fin-node-api\r\nhost: localhost:3000\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/fcrepo/rest',
_ended: true,
res: [Circular],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
[Symbol(outHeadersKey)]: [Object] },
request:
Request {
domain: null,
_events: [Object],
_eventsCount: 5,
_maxListeners: undefined,
method: 'GET',
headers: [Object],
uri: [Object],
callback: [Function],
readable: true,
writable: true,
explicitMethod: true,
_qs: [Object],
_auth: [Object],
_oauth: [Object],
_multipart: [Object],
_redirect: [Object],
_tunnel: [Object],
setHeader: [Function],
hasHeader: [Function],
getHeader: [Function],
removeHeader: [Function],
localAddress: undefined,
pool: {},
dests: [],
__isRequestRequest: true,
_callback: [Function],
proxy: null,
tunnel: false,
setHost: true,
originalCookieHeader: undefined,
_disableCookies: true,
_jar: undefined,
port: '3000',
host: 'localhost',
path: '/fcrepo/rest',
httpModule: [Object],
agentClass: [Object],
agent: [Object],
_started: true,
href: 'http://localhost:3000/fcrepo/rest',
req: [Object],
ntick: true,
response: [Circular],
originalHost: 'localhost:3000',
originalHostHeaderName: 'host',
responseContent: [Circular],
_destdata: true,
_ended: true,
_callbackCalled: true },
toJSON: [Function: responseToJSON],
caseless: Caseless { dict: [Object] },
read: [Function],
body: '<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>\n<title>Error 403 Forbidden</title>\n</head>\n<body><h2>HTTP ERROR 403</h2>\n<p>Problem accessing /fcrepo/rest. Reason:\n<pre> Forbidden</pre></p><hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.6.v20170531</a><hr/>\n\n</body>\n</html>\n',
finAuthenticated: true },
error: null }
You missed my note about Boolean values. The admin flag should not be a string.
@jrmerz I now have access to LDP and have connected our app to use the basic auth service. I am able to create users and login. I am able to see that the cookie has been set by the auth service. What I am confused about is the next step to creating different sessions for our application. Can you point me in the right direction please?
Great to hear! And happy to help on next steps, but can you elaborate on what you mean by:
What I am confused about is the next step to creating different sessions for our application?
So I am trying to make sure that we are allowing different sessions for different users.
So more than 1 user can access the app at once and their sessions will be completely separate
On Nov 16, 2018, at 8:06 AM, Justin Merz notifications@github.com wrote:
Great to hear! And happy to help on next steps, but can you elaborate on what you mean by:
What I am confused about is the next step to creating different sessions for our application?
— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub, or mute the thread.
@jmrez
I am also having trouble pushing data and creating collections in ldp.
Would you be able to meet sometime this coming week for me to pick your brain on a few things?
On Nov 16, 2018, at 8:06 AM, Justin Merz notifications@github.com wrote:
Great to hear! And happy to help on next steps, but can you elaborate on what you mean by:
What I am confused about is the next step to creating different sessions for our application?
— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub, or mute the thread.
@DerekMaggio Ya, your response to about sessions makes me think there is some badness in your setup.
I'm around Monday-Wednesday next week and my schedule is mostly open other than Monday morning. I can meetup online Mon/Tues, I could also come on campus on Wed (assuming things open up by then obviously) if you want to arrange that.
@jrmerz would a remote chat sometime Wednesday work for you? If we cannot get the questions answered tomorrow during our meeting?
Sure, time?
Does 9am work?
Yup, sounds good
@jrmerz Is there any prep work that you would suggest before the meeting? Any problems that you might suspect that you think would be easier for me to look into before the meeting?
The session is stored as a cookie, as you stated above. So to log a user out (or switch a user) you need to clear the cookie. This can be done by calling /auth/logout, which just clears the session cookie and removes the token and any associated session information stored in Redis server side. Thats it. To log a user in, you then call the login method again.
Cookies are stored in the browser and sent with every HTTP request. So multiple users should always be able to login via multiple browsers. https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
Past that, I'm not sure what 'What I am confused about is the next step to creating different sessions for our application?' means, so not sure how to help. Can you elaborate?
@jrmerz From that, do I then need to implement something like express-session so my application knows that we are using different sessions?
On Nov 20, 2018, at 8:27 AM, Justin Merz notifications@github.com wrote:
The session is stored as a cookie, as you stated above. So to log a user out (or switch a user) you need to clear the cookie. This can be done by calling /auth/logout, which just clears the session cookie and removes the token and any associated session information stored in Redis server side. Thats it. To log a user in, you then call the login method again.
Cookies are stored in the browser and sent with every HTTP request. So multiple users should always be able to login via multiple browsers. https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
Past that, I'm not sure what 'What I am confused about is the next step to creating different sessions for our application?' means, so not sure how to help. Can you elaborate?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@jrmerz during our meeting tomorrow 10-30 I would like to develop requirements for authentication and session management