An atproto PDS written in Go
at 0.3.4 2.8 kB view raw
1package server 2 3import ( 4 "time" 5 6 "github.com/Azure/go-autorest/autorest/to" 7 "github.com/haileyok/cocoon/internal/helpers" 8 "github.com/haileyok/cocoon/oauth" 9 "github.com/haileyok/cocoon/oauth/constants" 10 "github.com/haileyok/cocoon/oauth/provider" 11 "github.com/labstack/echo/v4" 12) 13 14type OauthParResponse struct { 15 ExpiresIn int64 `json:"expires_in"` 16 RequestURI string `json:"request_uri"` 17} 18 19func (s *Server) handleOauthPar(e echo.Context) error { 20 var parRequest provider.ParRequest 21 if err := e.Bind(&parRequest); err != nil { 22 s.logger.Error("error binding for par request", "error", err) 23 return helpers.ServerError(e, nil) 24 } 25 26 if err := e.Validate(parRequest); err != nil { 27 s.logger.Error("missing parameters for par request", "error", err) 28 return helpers.InputError(e, nil) 29 } 30 31 // TODO: this seems wrong. should be a way to get the entire request url i believe, but this will work for now 32 dpopProof, err := s.oauthProvider.DpopManager.CheckProof(e.Request().Method, "https://"+s.config.Hostname+e.Request().URL.String(), e.Request().Header, nil) 33 if err != nil { 34 s.logger.Error("error getting dpop proof", "error", err) 35 return helpers.InputError(e, to.StringPtr(err.Error())) 36 } 37 38 client, clientAuth, err := s.oauthProvider.AuthenticateClient(e.Request().Context(), parRequest.AuthenticateClientRequestBase, dpopProof, &provider.AuthenticateClientOptions{ 39 // rfc9449 40 // https://github.com/bluesky-social/atproto/blob/main/packages/oauth/oauth-provider/src/oauth-provider.ts#L473 41 AllowMissingDpopProof: true, 42 }) 43 if err != nil { 44 s.logger.Error("error authenticating client", "error", err) 45 return helpers.InputError(e, to.StringPtr(err.Error())) 46 } 47 48 if parRequest.DpopJkt == nil { 49 if client.Metadata.DpopBoundAccessTokens { 50 parRequest.DpopJkt = to.StringPtr(dpopProof.JKT) 51 } 52 } else { 53 if !client.Metadata.DpopBoundAccessTokens { 54 msg := "dpop bound access tokens are not enabled for this client" 55 s.logger.Error(msg) 56 return helpers.InputError(e, &msg) 57 } 58 59 if dpopProof.JKT != *parRequest.DpopJkt { 60 msg := "supplied dpop jkt does not match header dpop jkt" 61 s.logger.Error(msg) 62 return helpers.InputError(e, &msg) 63 } 64 } 65 66 eat := time.Now().Add(constants.ParExpiresIn) 67 id := oauth.GenerateRequestId() 68 69 authRequest := &provider.OauthAuthorizationRequest{ 70 RequestId: id, 71 ClientId: client.Metadata.ClientID, 72 ClientAuth: *clientAuth, 73 Parameters: parRequest, 74 ExpiresAt: eat, 75 } 76 77 if err := s.db.Create(authRequest, nil).Error; err != nil { 78 s.logger.Error("error creating auth request in db", "error", err) 79 return helpers.ServerError(e, nil) 80 } 81 82 uri := oauth.EncodeRequestUri(id) 83 84 return e.JSON(201, OauthParResponse{ 85 ExpiresIn: int64(constants.ParExpiresIn.Seconds()), 86 RequestURI: uri, 87 }) 88}