|     IEEE Xplore Digital Library     |     IEEE Standards     |     IEEE Spectrum     |     More Sites

Verified Commit b945905f authored by Emi Simpson's avatar Emi Simpson
Browse files

[api] Add expiration times to Session objects

parent 7b02a104
Pipeline #1087 passed with stage
in 49 seconds
......@@ -62,6 +62,8 @@
### Session
* **token:** `str` (18 bytes b64) - An authentication token that can be used for future logins
* **expires:** `int` (valid epoch millis) - The moment that the session token is set to expire
* **user:** `User` - The user who just logged in (equivilent to a call to `/whoami`)
### Errors
......@@ -236,7 +236,7 @@ class AuthModule:
on_error=lambda _: (
from typing import Collection, Literal, Optional, TypedDict
from mystic import coordination, queries
from mystic.queries import SessionTokenInfo
from mystic.types import Backend, UserID, ProjectID, SourceID, Url
from mystic.sources import SOURCE_PROCESSORS
......@@ -270,6 +271,11 @@ class Session(TypedDict):
length is guaranteed to be exactly 24 characters.
expires: int
The time at which this session will expire, in epoch millis
user: User
Information about the currently authenticated user
......@@ -277,9 +283,9 @@ class Session(TypedDict):
Equivilent to a call to /whoami
def create_session(token: bytes, user: User) -> Session:
def create_session(token: SessionTokenInfo, user: User) -> Session:
A shorthand for creating a :class:`Session` from an unencoded session token
from base64 import b64encode
return Session(token=b64encode(token).decode('ASCII'), user=user)
return Session(token=b64encode(token.token).decode('ASCII'), expires=token.expires, user=user)
......@@ -1117,6 +1117,20 @@ class GetSources(NamedTuple):
for source in results.all()
class SessionTokenInfo(NamedTuple):
Some simple information about a session
token: bytes
The token bytes themselves, exactly eighteen of them
expires: int
The exact millisecond the token will expire, in epoch millis
class CreateSession(NamedTuple):
A :class:`Query` to create a new session for a given user
......@@ -1124,7 +1138,9 @@ class CreateSession(NamedTuple):
By default, the session will be marked to expire in one week, although this can be
overriden using the :attr:`lifespan` parameter.
If successful, this will produce exactly an exactly 18-byte session token.
If successful, this will produce exactly an exactly 18-byte session token and the
timestamp it can be expected to expire (as epoch millis).
If the user with the provided ID does not exist, then this returns None
user_id: UserID
......@@ -1149,10 +1165,10 @@ class CreateSession(NamedTuple):
return QueryRequest('''
INSERT INTO sessions (token, user_id, expires)
RETURNING token, UNIX_TIMESTAMP(expires) * 1000;
''', (randbytes(18), self.user_id, self.lifespan))
def handle_results(self, results: QueryResult | SqlIntegrityError) -> Finished[bytes] | Unfinished[bytes, None] | Error[None]:
def handle_results(self, results: QueryResult | SqlIntegrityError) -> Finished[SessionTokenInfo] | Unfinished[SessionTokenInfo, None] | Error[None]:
if isinstance(results, SqlIntegrityError):
match results.error_code:
case SqlErrorCode.DUP_ENTRY:
......@@ -1164,11 +1180,12 @@ class CreateSession(NamedTuple):
case unknown:
raise Exception(f'Unexpected sql error: {unknown}')
records =
assert records is not None, 'This statement is expect to always produce one record'
token: bytes = records[0]
record: Tuple[bytes, int] | None =
assert record is not None, 'This statement is expect to always produce one record'
token, timestamp = record
assert isinstance(token, bytes), 'This statement is expected to return bytes'
return Finished(token)
assert isinstance(timestamp, int), 'This statement is expected to return a int'
return Finished(SessionTokenInfo(token, timestamp))
class InvalidateSession(NamedTuple):
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment