Skip to content

Commit

Permalink
feat: entpoint to delete login session by session id
Browse files Browse the repository at this point in the history
  • Loading branch information
aarmam committed Mar 30, 2022
1 parent 00100a1 commit 66dd1d1
Show file tree
Hide file tree
Showing 12 changed files with 789 additions and 2 deletions.
8 changes: 8 additions & 0 deletions consent/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ type swaggerRevokeAuthenticationSessionPayload struct {
Subject string `json:"subject"`
}

// swagger:parameters revokeAuthenticationSessionById
type swaggerRevokeAuthenticationSessionById struct {
// The id of the desired grant
// in: path
// required: true
ID string `json:"id"`
}

// swagger:parameters acceptLoginRequest
type swaggerAcceptLoginRequest struct {
// in: query
Expand Down
42 changes: 40 additions & 2 deletions consent/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func (h *Handler) SetRoutes(admin *x.RouterAdmin) {
admin.PUT(ConsentPath+"/accept", h.AcceptConsentRequest)
admin.PUT(ConsentPath+"/reject", h.RejectConsentRequest)

admin.DELETE(SessionsPath+"/login", h.DeleteLoginSession)
admin.DELETE(SessionsPath+"/login/:id", h.DeleteLoginSessionBySessionId)
admin.DELETE(SessionsPath+"/login", h.DeleteLoginSessionBySubject)
admin.GET(SessionsPath+"/consent", h.GetConsentSessions)
admin.DELETE(SessionsPath+"/consent", h.DeleteConsentSession)

Expand Down Expand Up @@ -189,6 +190,43 @@ func (h *Handler) GetConsentSessions(w http.ResponseWriter, r *http.Request, ps
h.r.Writer().Write(w, r, a)
}

// swagger:route DELETE /oauth2/auth/sessions/login/{id} admin revokeAuthenticationSessionById
//
// Invalidates an Authentication Session
//
// This endpoint invalidates an authentication session by session id. After revoking the authentication session, the
// subject has to re-authenticate at ORY Hydra for this device/browser. This endpoint does not invalidate any tokens
// and does not work with OpenID Connect Front- or Back-channel logout.
//
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 204: emptyResponse
// 400: jsonError
// 500: jsonError
func (h *Handler) DeleteLoginSessionBySessionId(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var loginSessionId = ps.ByName("id")

if loginSessionId == "" {
h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Path parameter 'id' is not defined but should have been.`)))
return
}

if err := h.r.ConsentManager().DeleteLoginSession(r.Context(), loginSessionId); err != nil && !errors.Is(err, x.ErrNotFound) {
h.r.Writer().WriteError(w, r, err)
return
}

w.WriteHeader(http.StatusNoContent)
}

// swagger:route DELETE /oauth2/auth/sessions/login admin revokeAuthenticationSession
//
// Invalidates All Login Sessions of a Certain User
Expand All @@ -211,7 +249,7 @@ func (h *Handler) GetConsentSessions(w http.ResponseWriter, r *http.Request, ps
// 204: emptyResponse
// 400: jsonError
// 500: jsonError
func (h *Handler) DeleteLoginSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
func (h *Handler) DeleteLoginSessionBySubject(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
subject := r.URL.Query().Get("subject")
if subject == "" {
h.r.Writer().WriteError(w, r, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter 'subject' is not defined but should have been.`)))
Expand Down
86 changes: 86 additions & 0 deletions consent/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/pborman/uuid"

"github.com/ory/x/sqlxx"

"github.com/ory/hydra/x"

Expand Down Expand Up @@ -213,3 +218,84 @@ func TestGetConsentRequest(t *testing.T) {
})
}
}

func TestDeleteLoginSessionBySessionId(t *testing.T) {
conf := internal.NewConfigurationWithDefaults()
reg := internal.NewRegistryMemory(t, conf)
loginSession1 := uuid.NewUUID().String()
require.NoError(t, reg.ConsentManager().CreateLoginSession(context.Background(), &LoginSession{
ID: loginSession1,
AuthenticatedAt: sqlxx.NullTime(time.Now().Round(time.Second).UTC()),
Subject: "subject1",
Remember: true,
}))
loginSession2 := uuid.NewUUID().String()
require.NoError(t, reg.ConsentManager().CreateLoginSession(context.Background(), &LoginSession{
ID: loginSession2,
AuthenticatedAt: sqlxx.NullTime(time.Now().Round(time.Second).UTC()),
Subject: "subject1",
Remember: true,
}))
h := NewHandler(reg, conf)
r := x.NewRouterAdmin()
h.SetRoutes(r)
ts := httptest.NewServer(r)
defer ts.Close()
_, err := reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession1)
require.NoError(t, err)
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession2)
require.NoError(t, err)
c := &http.Client{}

req, err := http.NewRequest("DELETE", ts.URL+SessionsPath+"/login/"+loginSession1, nil)

require.NoError(t, err)
resp, err := c.Do(req)
require.NoError(t, err)
require.EqualValues(t, http.StatusNoContent, resp.StatusCode)
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession1)
require.EqualError(t, err, x.ErrNotFound.Error())
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession2)
require.NoError(t, err)
}

func TestDeleteLoginSessionBySubject(t *testing.T) {
conf := internal.NewConfigurationWithDefaults()
reg := internal.NewRegistryMemory(t, conf)
subject := "subject1"
loginSession1 := uuid.NewUUID().String()
require.NoError(t, reg.ConsentManager().CreateLoginSession(context.Background(), &LoginSession{
ID: loginSession1,
AuthenticatedAt: sqlxx.NullTime(time.Now().Round(time.Second).UTC()),
Subject: subject,
Remember: true,
}))
loginSession2 := uuid.NewUUID().String()
require.NoError(t, reg.ConsentManager().CreateLoginSession(context.Background(), &LoginSession{
ID: loginSession2,
AuthenticatedAt: sqlxx.NullTime(time.Now().Round(time.Second).UTC()),
Subject: subject,
Remember: true,
}))
h := NewHandler(reg, conf)
r := x.NewRouterAdmin()
h.SetRoutes(r)
ts := httptest.NewServer(r)
defer ts.Close()
_, err := reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession1)
require.NoError(t, err)
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession2)
require.NoError(t, err)
c := &http.Client{}

req, err := http.NewRequest("DELETE", ts.URL+SessionsPath+"/login?subject="+subject, nil)

require.NoError(t, err)
resp, err := c.Do(req)
require.NoError(t, err)
require.EqualValues(t, http.StatusNoContent, resp.StatusCode)
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession1)
require.EqualError(t, err, x.ErrNotFound.Error())
_, err = reg.ConsentManager().GetRememberedLoginSession(context.Background(), loginSession2)
require.EqualError(t, err, x.ErrNotFound.Error())
}
1 change: 1 addition & 0 deletions internal/httpclient-next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ Class | Method | HTTP request | Description
*AdminApi* | [**RejectLoginRequest**](docs/AdminApi.md#rejectloginrequest) | **Put** /oauth2/auth/requests/login/reject | Reject a Login Request
*AdminApi* | [**RejectLogoutRequest**](docs/AdminApi.md#rejectlogoutrequest) | **Put** /oauth2/auth/requests/logout/reject | Reject a Logout Request
*AdminApi* | [**RevokeAuthenticationSession**](docs/AdminApi.md#revokeauthenticationsession) | **Delete** /oauth2/auth/sessions/login | Invalidates All Login Sessions of a Certain User Invalidates a Subject's Authentication Session
*AdminApi* | [**RevokeAuthenticationSessionById**](docs/AdminApi.md#revokeauthenticationsessionbyid) | **Delete** /oauth2/auth/sessions/login/{id} | Invalidates an Authentication Session
*AdminApi* | [**RevokeConsentSessions**](docs/AdminApi.md#revokeconsentsessions) | **Delete** /oauth2/auth/sessions/consent | Revokes Consent Sessions of a Subject for a Specific OAuth 2.0 Client
*AdminApi* | [**TrustJwtGrantIssuer**](docs/AdminApi.md#trustjwtgrantissuer) | **Post** /trust/grants/jwt-bearer/issuers | Trust an OAuth2 JWT Bearer Grant Type Issuer
*AdminApi* | [**UpdateJsonWebKey**](docs/AdminApi.md#updatejsonwebkey) | **Put** /keys/{set}/{kid} | Update a JSON Web Key
Expand Down
36 changes: 36 additions & 0 deletions internal/httpclient-next/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,42 @@ paths:
Invalidates a Subject's Authentication Session
tags:
- admin
/oauth2/auth/sessions/login/{id}:
delete:
description: |-
This endpoint invalidates an authentication session by session id. After revoking the authentication session, the
subject has to re-authenticate at ORY Hydra for this device/browser. This endpoint does not invalidate any tokens
and does not work with OpenID Connect Front- or Back-channel logout.
operationId: revokeAuthenticationSessionById
parameters:
- description: The id of the desired grant
explode: false
in: path
name: id
required: true
schema:
type: string
style: simple
responses:
"204":
description: |-
Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is
typically 201.
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/jsonError'
description: jsonError
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/jsonError'
description: jsonError
summary: Invalidates an Authentication Session
tags:
- admin
/oauth2/flush:
post:
description: |-
Expand Down
131 changes: 131 additions & 0 deletions internal/httpclient-next/api_admin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 66dd1d1

Please sign in to comment.