Skip to content

Commit

Permalink
Remove support for IRMA and EmployeeID signing means
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed May 13, 2024
1 parent 5f4bc67 commit be6aabb
Show file tree
Hide file tree
Showing 23 changed files with 575 additions and 2,830 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ gen-api:
-o nuts/client/vdr_v2/generated.go https://nuts-node.readthedocs.io/en/latest/_static/vdr/v2.yaml
oapi-codegen -generate client,types -package iam \
-import-mapping='../common/ssi_types.yaml:github.com/nuts-foundation/nuts-demo-ehr/nuts/client/common' \
-o nuts/client/iam/generated.go https://nuts-node.readthedocs.io/en/latest/_static/auth/iam.yaml
-o nuts/client/iam/generated.go https://nuts-node.readthedocs.io/en/latest/_static/auth/v2.yaml
10 changes: 0 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,6 @@ It's important to configure the Nuts node address in the `server.config.yaml`. T
nutsnodeaddr: "http://localhost:1323"
```
When using IRMA for authentication, the Nuts node will generate a QR code with an URL in it. This URL must be publicly accessible.
It can be configured IN THE NUTS NODE configuration file:
```yaml
auth:
publicurl: http://5d6670ee3d46.eu.ngrok.io
```
The above example uses [ngrok](https://ngrok.io) to proxy a ngrok URL to localhost:1323.
## Technology Stack
Frontend framework is vue.js 3.x
Expand Down
231 changes: 1 addition & 230 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (

"github.com/lestrrat-go/jwx/jwt"
nutsClient "github.com/nuts-foundation/nuts-demo-ehr/nuts/client"
nutsAuth "github.com/nuts-foundation/nuts-demo-ehr/nuts/client/auth"
"github.com/nuts-foundation/nuts-demo-ehr/nuts/registry"

"github.com/labstack/echo/v4"
Expand Down Expand Up @@ -102,7 +101,7 @@ func (w Wrapper) AuthenticateWithPassword(ctx echo.Context) error {
return ctx.JSON(http.StatusForbidden, errorResponse{err})
}

token, err := w.APIAuth.CreateSessionJWT(customer.Name, userInfo.Identifier, req.CustomerID, sessionId, false)
token, err := w.APIAuth.CreateSessionJWT(customer.Name, userInfo.Identifier, req.CustomerID, sessionId)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
Expand Down Expand Up @@ -146,234 +145,6 @@ func (w Wrapper) GetOpenID4VPAuthenticationResult(ctx echo.Context, token string
return ctx.JSON(http.StatusOK, response)
}

func (w Wrapper) AuthenticateWithIRMA(ctx echo.Context) error {
customerID, err := w.APIAuth.GetCustomerIDFromHeader(ctx)
if err != nil {
return err
}
customer, err := w.CustomerRepository.FindByID(customerID)
if err != nil {
return ctx.JSON(http.StatusInternalServerError, errorResponse{err})
}

// forward to node
bytes, err := w.NutsClient.CreateIrmaSession(*customer.Did)
if err != nil {
return err
}

// convert to map so echo rendering doesn't escape double quotes
j := map[string]interface{}{}
json.Unmarshal(bytes, &j)
return ctx.JSON(http.StatusOK, j)
}

func (w Wrapper) GetIRMAAuthenticationResult(ctx echo.Context, sessionToken string) error {
customerID, err := w.APIAuth.GetCustomerIDFromHeader(ctx)
if err != nil {
return err
}

// forward to node
sessionStatus, err := w.NutsClient.GetIrmaSessionResult(sessionToken)
if err != nil {
return err
}

if sessionStatus.Status != "DONE" {
return echo.NewHTTPError(http.StatusNotFound, "signing session not completed")
}

authSessionID, err := w.getSessionID(ctx)
if err != nil {
// No current session, create a new one. Introspect IRMA VP and extract properties for UserInfo.
userPresentation, err := w.NutsClient.VerifyPresentation(*sessionStatus.VerifiablePresentation)
if err != nil {
return fmt.Errorf("unable to verify presentation: %w", err)
}
attrs := *userPresentation.IssuerAttributes
userInfo := UserInfo{
Identifier: fmt.Sprintf("%v", attrs["sidn-pbdf.email.email"]),
Initials: fmt.Sprintf("%v", attrs["gemeente.personalData.initials"]),
FamilyName: fmt.Sprintf("%v", attrs["gemeente.personalData.familyname"]),
}
authSessionID = w.APIAuth.createSession(customerID, userInfo)
}

err = w.APIAuth.Elevate(authSessionID, *sessionStatus.VerifiablePresentation)
if err != nil {
return fmt.Errorf("unable to elevate session: %w", err)
}

session := w.APIAuth.GetSession(authSessionID)

customer, err := w.CustomerRepository.FindByID(customerID)
if err != nil {
return err
}

newToken, err := w.APIAuth.CreateSessionJWT(customer.Name, session.UserInfo.Identifier, customerID, authSessionID, true)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

return ctx.JSON(200, types.SessionToken{Token: string(newToken)})
}

func (w Wrapper) AuthenticateWithEmployeeID(ctx echo.Context) error {
// The method is called "Authenticate" but it is actually elevation,
// since it requires an existing session from with employee info.
var sessionID string
if sid, ok := ctx.Get(SessionID).(string); ok {
sessionID = sid
} else {
return echo.NewHTTPError(http.StatusUnauthorized, "existing session is required for EmployeeID means (missing token)")
}

session := w.APIAuth.GetSession(sessionID)
if session == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "existing session is required for EmployeeID means (unknown session)")
}

customer, _ := w.CustomerRepository.FindByID(session.CustomerID)
if customer == nil || customer.Did == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "customer with DID required for EmployeeID means")
}

params := map[string]interface{}{
"employer": *customer.Did,
"employee": map[string]interface{}{
"identifier": session.UserInfo.Identifier,
"roleName": session.UserInfo.RoleName,
"initials": session.UserInfo.Initials,
"familyName": session.UserInfo.FamilyName,
},
}
bytes, err := w.NutsClient.CreateEmployeeIDSession(params)
if err != nil {
return err
}

// convert to map so echo rendering doesn't escape double quotes
j := map[string]interface{}{}

if err := json.Unmarshal(bytes, &j); err != nil {
return err
}

return ctx.JSON(http.StatusOK, j)
}

func (w Wrapper) GetEmployeeIDAuthenticationResult(ctx echo.Context, sessionToken string) error {
authSession, err := w.getSession(ctx)
if err != nil {
return err
}
authSessionID, _ := w.getSessionID(ctx) // can't fail

// forward to node
sessionStatus, err := w.NutsClient.GetEmployeeIDSessionResult(sessionToken)
if err != nil {
return err
}

if sessionStatus.Status != "completed" {
return echo.NewHTTPError(http.StatusNotFound, sessionStatus.Status)
}

err = w.APIAuth.Elevate(authSessionID, *sessionStatus.VerifiablePresentation)
if err != nil {
return fmt.Errorf("unable to elevate session: %w", err)
}

customer, err := w.CustomerRepository.FindByID(authSession.CustomerID)
if err != nil {
return err
}

newToken, err := w.APIAuth.CreateSessionJWT(customer.Name, authSession.UserInfo.Identifier, authSession.CustomerID, authSessionID, true)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

return ctx.JSON(200, types.SessionToken{Token: string(newToken)})
}

func (w Wrapper) AuthenticateWithDummy(ctx echo.Context) error {
customerID, err := w.APIAuth.GetCustomerIDFromHeader(ctx)
if err != nil {
return err
}

customer, err := w.CustomerRepository.FindByID(customerID)
if err != nil {
return ctx.JSON(http.StatusInternalServerError, errorResponse{err})
}

bytes, err := w.NutsClient.CreateDummySession(*customer.Did)
if err != nil {
return err
}

// convert to map so echo rendering doesn't escape double quotes
j := map[string]interface{}{}

if err := json.Unmarshal(bytes, &j); err != nil {
return err
}

return ctx.JSON(http.StatusOK, j)
}

func (w Wrapper) GetDummyAuthenticationResult(ctx echo.Context, sessionToken string) error {
authSession, err := w.getSession(ctx)
if err != nil {
return err
}
authSessionID, _ := w.getSessionID(ctx) // can't fail

customerID, err := w.APIAuth.GetCustomerIDFromHeader(ctx)
if err != nil {
return err
}

var sessionResult *nutsAuth.SignSessionStatusResponse

// for dummy, it takes a few request to get to status completed.
for i := 0; i < 4; i++ {
// forward to node
sessionResult, err = w.NutsClient.GetDummySessionResult(sessionToken)
if err != nil {
return err
}

if sessionResult.Status == "completed" {
break
}
}

if sessionResult.Status != "completed" {
return echo.NewHTTPError(http.StatusNotFound, "signing session not completed")
}

err = w.APIAuth.Elevate(authSessionID, *sessionResult.VerifiablePresentation)
if err != nil {
return fmt.Errorf("failed to elevate session: %w", err)
}

customer, err := w.CustomerRepository.FindByID(customerID)
if err != nil {
return err
}

newToken, err := w.APIAuth.CreateSessionJWT(customer.Name, authSession.UserInfo.Identifier, customerID, authSessionID, true)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

return ctx.JSON(200, types.SessionToken{Token: string(newToken)})
}

func (w Wrapper) GetCustomer(ctx echo.Context) error {
customerID := ctx.Get(CustomerID)

Expand Down
93 changes: 0 additions & 93 deletions api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,99 +78,6 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/TokenResponse"

# IRMA authentication
/auth/irma/session:
post:
description: Create an IRMA signing session.
operationId: authenticateWithIRMA
responses:
'200':
description: An IRMA signing session was successfully created
content:
application/json:
schema:
type: object
/auth/irma/session/{sessionToken}/result:
parameters:
- name: sessionToken
in: path
description: IRMA session ID
required: true
schema:
type: string
get:
description: |
After a successful IRMA signing session the resulting signature can be fetched using his endpoint.
operationId: getIRMAAuthenticationResult
responses:
'200':
description: Session result
content:
application/json:
schema:
$ref: "#/components/schemas/SessionToken"
/auth/employeeid/session:
post:
description: Create an EmployeeID means signing session.
operationId: authenticateWithEmployeeID
responses:
'200':
description: An EmployeeID means signing session was successfully created
content:
application/json:
schema:
type: object
/auth/employeeid/session/{sessionToken}/result:
parameters:
- name: sessionToken
in: path
description: EmployeeID means session ID
required: true
schema:
type: string
get:
description: |
After a successful EmployeeID means signing session the resulting signature can be fetched using his endpoint.
operationId: getEmployeeIDAuthenticationResult
responses:
'200':
description: Session result
content:
application/json:
schema:
$ref: "#/components/schemas/SessionToken"
/auth/dummy:
post:
description: Create a dummy signing session.
operationId: authenticateWithDummy
responses:
'200':
description: A Dummy signing session was succesfully created
content:
application/json:
schema:
type: object
/auth/dummy/session/{sessionToken}/result:
parameters:
- name: sessionToken
in: path
description: Dummy session ID
required: true
schema:
type: string
get:
description: |
After a successful Dummy signing session the resulting token can be fetched using his endpoint.
operationId: getDummyAuthenticationResult
responses:
'200':
description: Session result
content:
application/json:
schema:
$ref: "#/components/schemas/SessionToken"

/private:
get:
description: Checks whether the current session is valid. If not, the client should authenticate before calling other API operations.
Expand Down
Loading

0 comments on commit be6aabb

Please sign in to comment.