-
Notifications
You must be signed in to change notification settings - Fork 140
CBG-4966 create a one time session id for blipsync #7845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Redocly previews |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements a one-time session ID mechanism for BlipSync authentication. The feature allows clients to create single-use session credentials that expire after first use or 5 minutes, whichever comes first.
Key changes:
- Added
one_timeparameter to the/db/_sessionendpoint that generates a one-time session with a 5-minute TTL - Implemented WebSocket protocol header authentication for BlipSync that extracts session IDs from
Sec-WebSocket-Protocolheaders - Enhanced session management to support one-time sessions that are automatically deleted upon successful authentication
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| rest/session_api.go | Refactored session creation to support one-time sessions with dedicated TTL and response formatting |
| rest/handler.go | Added WebSocket token extraction and one-time session authentication for BlipSync connections |
| rest/blip_sync.go | Defined constants for WebSocket protocol header and session ID prefix |
| rest/blip_sync_test.go | Added test coverage for one-time session BlipSync authentication flow |
| rest/oidc_api.go | Updated OIDC session creation to specify non-one-time session type |
| auth/session.go | Extended session model with OneTime field and implemented AuthenticateOneTimeSession method |
| auth/session_test.go | Added comprehensive test coverage for one-time session creation and authentication |
| docs/api/paths/public/db-_session.yaml | Updated OpenAPI specification with one_time parameter documentation |
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
adamcfraser
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good - a few comments/questions.
auth/session.go
Outdated
| if err != nil { | ||
| return nil, base.HTTPErrorf(http.StatusUnauthorized, "Session Invalid") | ||
| } | ||
| err = auth.datastore.Delete(auth.DocIDForSession(sessionID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like we should be checking for session.OneTime actually being set in the session here before deleting the session.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we let the session authenticate? I'm inclined to, but I could be flexible.
rest/handler.go
Outdated
| var outputHeaders []string | ||
| var sessionID string | ||
| for _, header := range strings.Split(protocolHeaders, ",") { | ||
| header := strings.TrimSpace(header) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TrimSpace here means that outputHeaders might not exactly match the incoming headers. I don't see how this would cause a problem, but it would be preferable to avoid changing anything other than the blipSessionIDPrefix header here?
|
|
||
| // NOTE: handleSessionPOST doesn't handle creating users from OIDC - checkPublicAuth calls out into AuthenticateUntrustedJWT. | ||
| // Therefore, if by this point `h.user` is guest, this isn't creating a session from OIDC. | ||
| if h.db.Options.DisablePasswordAuthentication && (h.user == nil || h.user.Name() == "") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This early return (and the associated comment) looks like it should be valid under the changes in this PR. Should we leave as-is to minimize risk of changes in behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can put this code back, but I had trouble with this case in unit tests which primarily existed to check the case where you could fall through and accidentally validate a user as a guest after removal of user, err := h.getUserFromSessionRequestBody()
I'm pretty confident this case is checked below because it is checked when params.Name != "" is checked but I don't mind keeping this either.
rest/session_api.go
Outdated
| } | ||
| user := h.user | ||
| if len(body) > 0 { | ||
| err := base.JSONUnmarshal(body, ¶ms) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason to not use readJSONInto here, as was previously done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readJSONInto will return an EOF error if there's no body - I want to read the body once.
But I'll switch to using ReadJSONFromMime to accomplish the same thing.
I couldn't even catch EOF error since
sync_gateway/rest/multipart.go
Line 48 in e886b08
| err = base.HTTPErrorf(http.StatusBadRequest, "Bad JSON: %s", err.Error()) |
rest/session_api.go
Outdated
| } | ||
| user := h.user | ||
| if len(body) > 0 { | ||
| err := ReadJSONFromMIME(h.rq.Header, io.NopCloser(bytes.NewReader(body)), ¶ms) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use h.requestBody here? (and avoid reading the body above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't detect if the body was empty or malformed using this because I can't identify the error from
sync_gateway/rest/multipart.go
Line 48 in e886b08
| err = base.HTTPErrorf(http.StatusBadRequest, "Bad JSON: %s", err.Error()) |
io.EOF.
Co-authored-by: Adam Fraser <[email protected]>
CBG-4966 create a one time session id for blipsync
one_time: trueparameter for public/db/_sessionend point that will return a one time session inone_time_session_idproperty/ks/_blipsynccheckSec-WebSocket-Protocolheader for a valueSyncGatewaySession={sessionID}. If found, use this for auth. Modifyhttp.Request.Headerto remove this header to remove this from logging https://github.com/couchbase/go-blip/blob/main/context.go#L256. It isn't harmful to keep it in, it will just get logged if a client connects to blip that doesn't speak any of the supported protocols. At that point the session will be invalid.I'm not sure I like the refactoring I did in
handleSessionPOST, butgetUserFromSessionRequestBodywas hiding a lot of errors, including guest auth. There might be a less risky way to do that, and I'm open to going over it again.Blocks https://github.com/couchbase/couchbase-lite-js/pull/67
Pre-review checklist
fmt.Print,log.Print, ...)base.UD(docID),base.MD(dbName))docs/apiDependencies (if applicable)
Integration Tests
GSI=true,xattrs=truehttps://jenkins.sgwdev.com/job/SyncGatewayIntegration/0000/