Merge pull request 'feat(session): add passkey list and remove RPCs' (#7) from feat/passkeys into main
Reviewed-on: https://git.bnema.dev/bnema/ermite-api/pulls/7
3 files · +420 -19
M gen/ermite/ermiteconnect/session.connect.go +70 -0
| @@ -41,6 +41,12 @@ const ( | |||
| 41 | 41 | // SessionServiceSignOutEverywhereProcedure is the fully-qualified name of the SessionService's | |
| 42 | 42 | // SignOutEverywhere RPC. | |
| 43 | 43 | SessionServiceSignOutEverywhereProcedure = "/ermite.SessionService/SignOutEverywhere" | |
| 44 | + | // SessionServiceListPasskeysProcedure is the fully-qualified name of the SessionService's | |
| 45 | + | // ListPasskeys RPC. | |
| 46 | + | SessionServiceListPasskeysProcedure = "/ermite.SessionService/ListPasskeys" | |
| 47 | + | // SessionServiceRemovePasskeyProcedure is the fully-qualified name of the SessionService's | |
| 48 | + | // RemovePasskey RPC. | |
| 49 | + | SessionServiceRemovePasskeyProcedure = "/ermite.SessionService/RemovePasskey" | |
| 44 | 50 | ) | |
| 45 | 51 | ||
| 46 | 52 | // SessionServiceClient is a client for the ermite.SessionService service. | |
| @@ -56,6 +62,14 @@ type SessionServiceClient interface { | |||
| 56 | 62 | // other devices. Pending login links are not affected. Tokens are not | |
| 57 | 63 | // affected either: revoke them with TokenService. | |
| 58 | 64 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | |
| 65 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |
| 66 | + | // with failed_precondition when the server has passkeys disabled. | |
| 67 | + | ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) | |
| 68 | + | // RemovePasskey removes one passkey. It also signs out every browser | |
| 69 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |
| 70 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |
| 71 | + | // failed_precondition when the server has passkeys disabled. | |
| 72 | + | RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) | |
| 59 | 73 | } | |
| 60 | 74 | ||
| 61 | 75 | // NewSessionServiceClient constructs a client for the ermite.SessionService service. By default, it | |
| @@ -81,6 +95,18 @@ func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts | |||
| 81 | 95 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | |
| 82 | 96 | connect.WithClientOptions(opts...), | |
| 83 | 97 | ), | |
| 98 | + | listPasskeys: connect.NewClient[ermite.ListPasskeysRequest, ermite.ListPasskeysResponse]( | |
| 99 | + | httpClient, | |
| 100 | + | baseURL+SessionServiceListPasskeysProcedure, | |
| 101 | + | connect.WithSchema(sessionServiceMethods.ByName("ListPasskeys")), | |
| 102 | + | connect.WithClientOptions(opts...), | |
| 103 | + | ), | |
| 104 | + | removePasskey: connect.NewClient[ermite.RemovePasskeyRequest, ermite.RemovePasskeyResponse]( | |
| 105 | + | httpClient, | |
| 106 | + | baseURL+SessionServiceRemovePasskeyProcedure, | |
| 107 | + | connect.WithSchema(sessionServiceMethods.ByName("RemovePasskey")), | |
| 108 | + | connect.WithClientOptions(opts...), | |
| 109 | + | ), | |
| 84 | 110 | } | |
| 85 | 111 | } | |
| 86 | 112 | ||
| @@ -88,6 +114,8 @@ func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts | |||
| 88 | 114 | type sessionServiceClient struct { | |
| 89 | 115 | createLoginLink *connect.Client[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse] | |
| 90 | 116 | signOutEverywhere *connect.Client[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse] | |
| 117 | + | listPasskeys *connect.Client[ermite.ListPasskeysRequest, ermite.ListPasskeysResponse] | |
| 118 | + | removePasskey *connect.Client[ermite.RemovePasskeyRequest, ermite.RemovePasskeyResponse] | |
| 91 | 119 | } | |
| 92 | 120 | ||
| 93 | 121 | // CreateLoginLink calls ermite.SessionService.CreateLoginLink. | |
| @@ -100,6 +128,16 @@ func (c *sessionServiceClient) SignOutEverywhere(ctx context.Context, req *conne | |||
| 100 | 128 | return c.signOutEverywhere.CallUnary(ctx, req) | |
| 101 | 129 | } | |
| 102 | 130 | ||
| 131 | + | // ListPasskeys calls ermite.SessionService.ListPasskeys. | |
| 132 | + | func (c *sessionServiceClient) ListPasskeys(ctx context.Context, req *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) { | |
| 133 | + | return c.listPasskeys.CallUnary(ctx, req) | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | // RemovePasskey calls ermite.SessionService.RemovePasskey. | |
| 137 | + | func (c *sessionServiceClient) RemovePasskey(ctx context.Context, req *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) { | |
| 138 | + | return c.removePasskey.CallUnary(ctx, req) | |
| 139 | + | } | |
| 140 | + | ||
| 103 | 141 | // SessionServiceHandler is an implementation of the ermite.SessionService service. | |
| 104 | 142 | type SessionServiceHandler interface { | |
| 105 | 143 | // CreateLoginLink creates a one-time link that signs the owner's browser | |
| @@ -113,6 +151,14 @@ type SessionServiceHandler interface { | |||
| 113 | 151 | // other devices. Pending login links are not affected. Tokens are not | |
| 114 | 152 | // affected either: revoke them with TokenService. | |
| 115 | 153 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | |
| 154 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |
| 155 | + | // with failed_precondition when the server has passkeys disabled. | |
| 156 | + | ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) | |
| 157 | + | // RemovePasskey removes one passkey. It also signs out every browser | |
| 158 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |
| 159 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |
| 160 | + | // failed_precondition when the server has passkeys disabled. | |
| 161 | + | RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) | |
| 116 | 162 | } | |
| 117 | 163 | ||
| 118 | 164 | // NewSessionServiceHandler builds an HTTP handler from the service implementation. It returns the | |
| @@ -134,12 +180,28 @@ func NewSessionServiceHandler(svc SessionServiceHandler, opts ...connect.Handler | |||
| 134 | 180 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | |
| 135 | 181 | connect.WithHandlerOptions(opts...), | |
| 136 | 182 | ) | |
| 183 | + | sessionServiceListPasskeysHandler := connect.NewUnaryHandler( | |
| 184 | + | SessionServiceListPasskeysProcedure, | |
| 185 | + | svc.ListPasskeys, | |
| 186 | + | connect.WithSchema(sessionServiceMethods.ByName("ListPasskeys")), | |
| 187 | + | connect.WithHandlerOptions(opts...), | |
| 188 | + | ) | |
| 189 | + | sessionServiceRemovePasskeyHandler := connect.NewUnaryHandler( | |
| 190 | + | SessionServiceRemovePasskeyProcedure, | |
| 191 | + | svc.RemovePasskey, | |
| 192 | + | connect.WithSchema(sessionServiceMethods.ByName("RemovePasskey")), | |
| 193 | + | connect.WithHandlerOptions(opts...), | |
| 194 | + | ) | |
| 137 | 195 | return "/ermite.SessionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 138 | 196 | switch r.URL.Path { | |
| 139 | 197 | case SessionServiceCreateLoginLinkProcedure: | |
| 140 | 198 | sessionServiceCreateLoginLinkHandler.ServeHTTP(w, r) | |
| 141 | 199 | case SessionServiceSignOutEverywhereProcedure: | |
| 142 | 200 | sessionServiceSignOutEverywhereHandler.ServeHTTP(w, r) | |
| 201 | + | case SessionServiceListPasskeysProcedure: | |
| 202 | + | sessionServiceListPasskeysHandler.ServeHTTP(w, r) | |
| 203 | + | case SessionServiceRemovePasskeyProcedure: | |
| 204 | + | sessionServiceRemovePasskeyHandler.ServeHTTP(w, r) | |
| 143 | 205 | default: | |
| 144 | 206 | http.NotFound(w, r) | |
| 145 | 207 | } | |
| @@ -156,3 +218,11 @@ func (UnimplementedSessionServiceHandler) CreateLoginLink(context.Context, *conn | |||
| 156 | 218 | func (UnimplementedSessionServiceHandler) SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) { | |
| 157 | 219 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.SignOutEverywhere is not implemented")) | |
| 158 | 220 | } | |
| 221 | + | ||
| 222 | + | func (UnimplementedSessionServiceHandler) ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) { | |
| 223 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.ListPasskeys is not implemented")) | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | func (UnimplementedSessionServiceHandler) RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) { | |
| 227 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.RemovePasskey is not implemented")) | |
| 228 | + | } | |
| @@ -41,6 +41,12 @@ const ( | |||||
| 41 | // SessionServiceSignOutEverywhereProcedure is the fully-qualified name of the SessionService's | 41 | // SessionServiceSignOutEverywhereProcedure is the fully-qualified name of the SessionService's | ||
| 42 | // SignOutEverywhere RPC. | 42 | // SignOutEverywhere RPC. | ||
| 43 | SessionServiceSignOutEverywhereProcedure = "/ermite.SessionService/SignOutEverywhere" | 43 | SessionServiceSignOutEverywhereProcedure = "/ermite.SessionService/SignOutEverywhere" | ||
| 44 | + | // SessionServiceListPasskeysProcedure is the fully-qualified name of the SessionService's | |||
| 45 | + | // ListPasskeys RPC. | |||
| 46 | + | SessionServiceListPasskeysProcedure = "/ermite.SessionService/ListPasskeys" | |||
| 47 | + | // SessionServiceRemovePasskeyProcedure is the fully-qualified name of the SessionService's | |||
| 48 | + | // RemovePasskey RPC. | |||
| 49 | + | SessionServiceRemovePasskeyProcedure = "/ermite.SessionService/RemovePasskey" | |||
| 44 | ) | 50 | ) | ||
| 45 | 51 | ||||
| 46 | // SessionServiceClient is a client for the ermite.SessionService service. | 52 | // SessionServiceClient is a client for the ermite.SessionService service. | ||
| @@ -56,6 +62,14 @@ type SessionServiceClient interface { | |||||
| 56 | // other devices. Pending login links are not affected. Tokens are not | 62 | // other devices. Pending login links are not affected. Tokens are not | ||
| 57 | // affected either: revoke them with TokenService. | 63 | // affected either: revoke them with TokenService. | ||
| 58 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | 64 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | ||
| 65 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |||
| 66 | + | // with failed_precondition when the server has passkeys disabled. | |||
| 67 | + | ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) | |||
| 68 | + | // RemovePasskey removes one passkey. It also signs out every browser | |||
| 69 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |||
| 70 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |||
| 71 | + | // failed_precondition when the server has passkeys disabled. | |||
| 72 | + | RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) | |||
| 59 | } | 73 | } | ||
| 60 | 74 | ||||
| 61 | // NewSessionServiceClient constructs a client for the ermite.SessionService service. By default, it | 75 | // NewSessionServiceClient constructs a client for the ermite.SessionService service. By default, it | ||
| @@ -81,6 +95,18 @@ func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts | |||||
| 81 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | 95 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | ||
| 82 | connect.WithClientOptions(opts...), | 96 | connect.WithClientOptions(opts...), | ||
| 83 | ), | 97 | ), | ||
| 98 | + | listPasskeys: connect.NewClient[ermite.ListPasskeysRequest, ermite.ListPasskeysResponse]( | |||
| 99 | + | httpClient, | |||
| 100 | + | baseURL+SessionServiceListPasskeysProcedure, | |||
| 101 | + | connect.WithSchema(sessionServiceMethods.ByName("ListPasskeys")), | |||
| 102 | + | connect.WithClientOptions(opts...), | |||
| 103 | + | ), | |||
| 104 | + | removePasskey: connect.NewClient[ermite.RemovePasskeyRequest, ermite.RemovePasskeyResponse]( | |||
| 105 | + | httpClient, | |||
| 106 | + | baseURL+SessionServiceRemovePasskeyProcedure, | |||
| 107 | + | connect.WithSchema(sessionServiceMethods.ByName("RemovePasskey")), | |||
| 108 | + | connect.WithClientOptions(opts...), | |||
| 109 | + | ), | |||
| 84 | } | 110 | } | ||
| 85 | } | 111 | } | ||
| 86 | 112 | ||||
| @@ -88,6 +114,8 @@ func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts | |||||
| 88 | type sessionServiceClient struct { | 114 | type sessionServiceClient struct { | ||
| 89 | createLoginLink *connect.Client[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse] | 115 | createLoginLink *connect.Client[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse] | ||
| 90 | signOutEverywhere *connect.Client[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse] | 116 | signOutEverywhere *connect.Client[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse] | ||
| 117 | + | listPasskeys *connect.Client[ermite.ListPasskeysRequest, ermite.ListPasskeysResponse] | |||
| 118 | + | removePasskey *connect.Client[ermite.RemovePasskeyRequest, ermite.RemovePasskeyResponse] | |||
| 91 | } | 119 | } | ||
| 92 | 120 | ||||
| 93 | // CreateLoginLink calls ermite.SessionService.CreateLoginLink. | 121 | // CreateLoginLink calls ermite.SessionService.CreateLoginLink. | ||
| @@ -100,6 +128,16 @@ func (c *sessionServiceClient) SignOutEverywhere(ctx context.Context, req *conne | |||||
| 100 | return c.signOutEverywhere.CallUnary(ctx, req) | 128 | return c.signOutEverywhere.CallUnary(ctx, req) | ||
| 101 | } | 129 | } | ||
| 102 | 130 | ||||
| 131 | + | // ListPasskeys calls ermite.SessionService.ListPasskeys. | |||
| 132 | + | func (c *sessionServiceClient) ListPasskeys(ctx context.Context, req *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) { | |||
| 133 | + | return c.listPasskeys.CallUnary(ctx, req) | |||
| 134 | + | } | |||
| 135 | + | ||||
| 136 | + | // RemovePasskey calls ermite.SessionService.RemovePasskey. | |||
| 137 | + | func (c *sessionServiceClient) RemovePasskey(ctx context.Context, req *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) { | |||
| 138 | + | return c.removePasskey.CallUnary(ctx, req) | |||
| 139 | + | } | |||
| 140 | + | ||||
| 103 | // SessionServiceHandler is an implementation of the ermite.SessionService service. | 141 | // SessionServiceHandler is an implementation of the ermite.SessionService service. | ||
| 104 | type SessionServiceHandler interface { | 142 | type SessionServiceHandler interface { | ||
| 105 | // CreateLoginLink creates a one-time link that signs the owner's browser | 143 | // CreateLoginLink creates a one-time link that signs the owner's browser | ||
| @@ -113,6 +151,14 @@ type SessionServiceHandler interface { | |||||
| 113 | // other devices. Pending login links are not affected. Tokens are not | 151 | // other devices. Pending login links are not affected. Tokens are not | ||
| 114 | // affected either: revoke them with TokenService. | 152 | // affected either: revoke them with TokenService. | ||
| 115 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | 153 | SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) | ||
| 154 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |||
| 155 | + | // with failed_precondition when the server has passkeys disabled. | |||
| 156 | + | ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) | |||
| 157 | + | // RemovePasskey removes one passkey. It also signs out every browser | |||
| 158 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |||
| 159 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |||
| 160 | + | // failed_precondition when the server has passkeys disabled. | |||
| 161 | + | RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) | |||
| 116 | } | 162 | } | ||
| 117 | 163 | ||||
| 118 | // NewSessionServiceHandler builds an HTTP handler from the service implementation. It returns the | 164 | // NewSessionServiceHandler builds an HTTP handler from the service implementation. It returns the | ||
| @@ -134,12 +180,28 @@ func NewSessionServiceHandler(svc SessionServiceHandler, opts ...connect.Handler | |||||
| 134 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | 180 | connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")), | ||
| 135 | connect.WithHandlerOptions(opts...), | 181 | connect.WithHandlerOptions(opts...), | ||
| 136 | ) | 182 | ) | ||
| 183 | + | sessionServiceListPasskeysHandler := connect.NewUnaryHandler( | |||
| 184 | + | SessionServiceListPasskeysProcedure, | |||
| 185 | + | svc.ListPasskeys, | |||
| 186 | + | connect.WithSchema(sessionServiceMethods.ByName("ListPasskeys")), | |||
| 187 | + | connect.WithHandlerOptions(opts...), | |||
| 188 | + | ) | |||
| 189 | + | sessionServiceRemovePasskeyHandler := connect.NewUnaryHandler( | |||
| 190 | + | SessionServiceRemovePasskeyProcedure, | |||
| 191 | + | svc.RemovePasskey, | |||
| 192 | + | connect.WithSchema(sessionServiceMethods.ByName("RemovePasskey")), | |||
| 193 | + | connect.WithHandlerOptions(opts...), | |||
| 194 | + | ) | |||
| 137 | return "/ermite.SessionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 195 | return "/ermite.SessionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| 138 | switch r.URL.Path { | 196 | switch r.URL.Path { | ||
| 139 | case SessionServiceCreateLoginLinkProcedure: | 197 | case SessionServiceCreateLoginLinkProcedure: | ||
| 140 | sessionServiceCreateLoginLinkHandler.ServeHTTP(w, r) | 198 | sessionServiceCreateLoginLinkHandler.ServeHTTP(w, r) | ||
| 141 | case SessionServiceSignOutEverywhereProcedure: | 199 | case SessionServiceSignOutEverywhereProcedure: | ||
| 142 | sessionServiceSignOutEverywhereHandler.ServeHTTP(w, r) | 200 | sessionServiceSignOutEverywhereHandler.ServeHTTP(w, r) | ||
| 201 | + | case SessionServiceListPasskeysProcedure: | |||
| 202 | + | sessionServiceListPasskeysHandler.ServeHTTP(w, r) | |||
| 203 | + | case SessionServiceRemovePasskeyProcedure: | |||
| 204 | + | sessionServiceRemovePasskeyHandler.ServeHTTP(w, r) | |||
| 143 | default: | 205 | default: | ||
| 144 | http.NotFound(w, r) | 206 | http.NotFound(w, r) | ||
| 145 | } | 207 | } | ||
| @@ -156,3 +218,11 @@ func (UnimplementedSessionServiceHandler) CreateLoginLink(context.Context, *conn | |||||
| 156 | func (UnimplementedSessionServiceHandler) SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) { | 218 | func (UnimplementedSessionServiceHandler) SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) { | ||
| 157 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.SignOutEverywhere is not implemented")) | 219 | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.SignOutEverywhere is not implemented")) | ||
| 158 | } | 220 | } | ||
| 221 | + | ||||
| 222 | + | func (UnimplementedSessionServiceHandler) ListPasskeys(context.Context, *connect.Request[ermite.ListPasskeysRequest]) (*connect.Response[ermite.ListPasskeysResponse], error) { | |||
| 223 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.ListPasskeys is not implemented")) | |||
| 224 | + | } | |||
| 225 | + | ||||
| 226 | + | func (UnimplementedSessionServiceHandler) RemovePasskey(context.Context, *connect.Request[ermite.RemovePasskeyRequest]) (*connect.Response[ermite.RemovePasskeyResponse], error) { | |||
| 227 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.RemovePasskey is not implemented")) | |||
| 228 | + | } | |||
M gen/ermite/session.pb.go +296 -15 generated file: collapsed
M proto/ermite/session.proto +54 -4
| @@ -6,9 +6,12 @@ package ermite; | |||
| 6 | 6 | ||
| 7 | 7 | import "google/protobuf/timestamp.proto"; | |
| 8 | 8 | ||
| 9 | - | // SessionService manages the owner's browser sessions. The browser itself | |
| 10 | - | // never calls it: it signs in by opening the link that CreateLoginLink | |
| 11 | - | // returns, and signs out by posting to the web's logout route. | |
| 9 | + | // SessionService manages the owner's browser sessions and passkeys. The | |
| 10 | + | // browser itself never calls it: it signs in by opening the link that | |
| 11 | + | // CreateLoginLink returns, or with a passkey on the web's sign-in page, and | |
| 12 | + | // signs out by posting to the web's logout route. Registering a passkey and | |
| 13 | + | // signing in with one are browser-only ceremonies and are not part of this | |
| 14 | + | // API. | |
| 12 | 15 | // | |
| 13 | 16 | // Every RPC is owner-only: it needs the root token, or a stored token that | |
| 14 | 17 | // holds every scope and is not restricted to repositories. Any other caller | |
| @@ -16,7 +19,9 @@ import "google/protobuf/timestamp.proto"; | |||
| 16 | 19 | // API accepts bearer tokens only and ignores cookies. | |
| 17 | 20 | // | |
| 18 | 21 | // Servers that implement this service list "session" in | |
| 19 | - | // GetVersionResponse.features. | |
| 22 | + | // GetVersionResponse.features. They also list "passkeys" when passkeys are | |
| 23 | + | // available (they need an https public URL with a DNS host name); without it, | |
| 24 | + | // ListPasskeys and RemovePasskey fail with failed_precondition. | |
| 20 | 25 | service SessionService { | |
| 21 | 26 | // CreateLoginLink creates a one-time link that signs the owner's browser | |
| 22 | 27 | // in. The link is valid for a short time (about a minute), can be used | |
| @@ -29,6 +34,14 @@ service SessionService { | |||
| 29 | 34 | // other devices. Pending login links are not affected. Tokens are not | |
| 30 | 35 | // affected either: revoke them with TokenService. | |
| 31 | 36 | rpc SignOutEverywhere(SignOutEverywhereRequest) returns (SignOutEverywhereResponse); | |
| 37 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |
| 38 | + | // with failed_precondition when the server has passkeys disabled. | |
| 39 | + | rpc ListPasskeys(ListPasskeysRequest) returns (ListPasskeysResponse); | |
| 40 | + | // RemovePasskey removes one passkey. It also signs out every browser | |
| 41 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |
| 42 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |
| 43 | + | // failed_precondition when the server has passkeys disabled. | |
| 44 | + | rpc RemovePasskey(RemovePasskeyRequest) returns (RemovePasskeyResponse); | |
| 32 | 45 | } | |
| 33 | 46 | ||
| 34 | 47 | // CreateLoginLinkRequest is empty. | |
| @@ -49,3 +62,40 @@ message SignOutEverywhereRequest {} | |||
| 49 | 62 | ||
| 50 | 63 | // SignOutEverywhereResponse is empty. | |
| 51 | 64 | message SignOutEverywhereResponse {} | |
| 65 | + | ||
| 66 | + | // Passkey is a WebAuthn credential that can sign the owner in. It never | |
| 67 | + | // carries key material. | |
| 68 | + | message Passkey { | |
| 69 | + | // Id is the credential id, base64url without padding. Pass it to | |
| 70 | + | // RemovePasskey. | |
| 71 | + | string id = 1; | |
| 72 | + | // Name is the label the owner gave the passkey when registering it. | |
| 73 | + | string name = 2; | |
| 74 | + | // CreatedAt is when the passkey was registered. | |
| 75 | + | google.protobuf.Timestamp created_at = 3; | |
| 76 | + | // LastUsedAt is when the passkey last signed the owner in. It is unset | |
| 77 | + | // when the passkey was never used. | |
| 78 | + | google.protobuf.Timestamp last_used_at = 4; | |
| 79 | + | // Synced is true when the authenticator reports the credential as backed | |
| 80 | + | // up (the backup state flag), for example a passkey synced by a platform or | |
| 81 | + | // a password manager. | |
| 82 | + | bool synced = 5; | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | // ListPasskeysRequest is empty. | |
| 86 | + | message ListPasskeysRequest {} | |
| 87 | + | ||
| 88 | + | // ListPasskeysResponse holds the passkeys. | |
| 89 | + | message ListPasskeysResponse { | |
| 90 | + | // Passkeys are the registered passkeys, oldest first. | |
| 91 | + | repeated Passkey passkeys = 1; | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | // RemovePasskeyRequest names the passkey to remove. | |
| 95 | + | message RemovePasskeyRequest { | |
| 96 | + | // Id is the credential id from Passkey.id. | |
| 97 | + | string id = 1; | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | // RemovePasskeyResponse is empty. | |
| 101 | + | message RemovePasskeyResponse {} | |
| @@ -6,9 +6,12 @@ package ermite; | |||||
| 6 | 6 | ||||
| 7 | import "google/protobuf/timestamp.proto"; | 7 | import "google/protobuf/timestamp.proto"; | ||
| 8 | 8 | ||||
| 9 | - | // SessionService manages the owner's browser sessions. The browser itself | 9 | + | // SessionService manages the owner's browser sessions and passkeys. The |
| 10 | - | // never calls it: it signs in by opening the link that CreateLoginLink | 10 | + | // browser itself never calls it: it signs in by opening the link that |
| 11 | - | // returns, and signs out by posting to the web's logout route. | 11 | + | // CreateLoginLink returns, or with a passkey on the web's sign-in page, and |
| 12 | + | // signs out by posting to the web's logout route. Registering a passkey and | |||
| 13 | + | // signing in with one are browser-only ceremonies and are not part of this | |||
| 14 | + | // API. | |||
| 12 | // | 15 | // | ||
| 13 | // Every RPC is owner-only: it needs the root token, or a stored token that | 16 | // Every RPC is owner-only: it needs the root token, or a stored token that | ||
| 14 | // holds every scope and is not restricted to repositories. Any other caller | 17 | // holds every scope and is not restricted to repositories. Any other caller | ||
| @@ -16,7 +19,9 @@ import "google/protobuf/timestamp.proto"; | |||||
| 16 | // API accepts bearer tokens only and ignores cookies. | 19 | // API accepts bearer tokens only and ignores cookies. | ||
| 17 | // | 20 | // | ||
| 18 | // Servers that implement this service list "session" in | 21 | // Servers that implement this service list "session" in | ||
| 19 | - | // GetVersionResponse.features. | 22 | + | // GetVersionResponse.features. They also list "passkeys" when passkeys are |
| 23 | + | // available (they need an https public URL with a DNS host name); without it, | |||
| 24 | + | // ListPasskeys and RemovePasskey fail with failed_precondition. | |||
| 20 | service SessionService { | 25 | service SessionService { | ||
| 21 | // CreateLoginLink creates a one-time link that signs the owner's browser | 26 | // CreateLoginLink creates a one-time link that signs the owner's browser | ||
| 22 | // in. The link is valid for a short time (about a minute), can be used | 27 | // in. The link is valid for a short time (about a minute), can be used | ||
| @@ -29,6 +34,14 @@ service SessionService { | |||||
| 29 | // other devices. Pending login links are not affected. Tokens are not | 34 | // other devices. Pending login links are not affected. Tokens are not | ||
| 30 | // affected either: revoke them with TokenService. | 35 | // affected either: revoke them with TokenService. | ||
| 31 | rpc SignOutEverywhere(SignOutEverywhereRequest) returns (SignOutEverywhereResponse); | 36 | rpc SignOutEverywhere(SignOutEverywhereRequest) returns (SignOutEverywhereResponse); | ||
| 37 | + | // ListPasskeys lists the owner's registered passkeys, oldest first. Fails | |||
| 38 | + | // with failed_precondition when the server has passkeys disabled. | |||
| 39 | + | rpc ListPasskeys(ListPasskeysRequest) returns (ListPasskeysResponse); | |||
| 40 | + | // RemovePasskey removes one passkey. It also signs out every browser | |||
| 41 | + | // session, on every device, like SignOutEverywhere. Fails with not_found | |||
| 42 | + | // for an unknown id, invalid_argument for an id that is not base64url, and | |||
| 43 | + | // failed_precondition when the server has passkeys disabled. | |||
| 44 | + | rpc RemovePasskey(RemovePasskeyRequest) returns (RemovePasskeyResponse); | |||
| 32 | } | 45 | } | ||
| 33 | 46 | ||||
| 34 | // CreateLoginLinkRequest is empty. | 47 | // CreateLoginLinkRequest is empty. | ||
| @@ -49,3 +62,40 @@ message SignOutEverywhereRequest {} | |||||
| 49 | 62 | ||||
| 50 | // SignOutEverywhereResponse is empty. | 63 | // SignOutEverywhereResponse is empty. | ||
| 51 | message SignOutEverywhereResponse {} | 64 | message SignOutEverywhereResponse {} | ||
| 65 | + | ||||
| 66 | + | // Passkey is a WebAuthn credential that can sign the owner in. It never | |||
| 67 | + | // carries key material. | |||
| 68 | + | message Passkey { | |||
| 69 | + | // Id is the credential id, base64url without padding. Pass it to | |||
| 70 | + | // RemovePasskey. | |||
| 71 | + | string id = 1; | |||
| 72 | + | // Name is the label the owner gave the passkey when registering it. | |||
| 73 | + | string name = 2; | |||
| 74 | + | // CreatedAt is when the passkey was registered. | |||
| 75 | + | google.protobuf.Timestamp created_at = 3; | |||
| 76 | + | // LastUsedAt is when the passkey last signed the owner in. It is unset | |||
| 77 | + | // when the passkey was never used. | |||
| 78 | + | google.protobuf.Timestamp last_used_at = 4; | |||
| 79 | + | // Synced is true when the authenticator reports the credential as backed | |||
| 80 | + | // up (the backup state flag), for example a passkey synced by a platform or | |||
| 81 | + | // a password manager. | |||
| 82 | + | bool synced = 5; | |||
| 83 | + | } | |||
| 84 | + | ||||
| 85 | + | // ListPasskeysRequest is empty. | |||
| 86 | + | message ListPasskeysRequest {} | |||
| 87 | + | ||||
| 88 | + | // ListPasskeysResponse holds the passkeys. | |||
| 89 | + | message ListPasskeysResponse { | |||
| 90 | + | // Passkeys are the registered passkeys, oldest first. | |||
| 91 | + | repeated Passkey passkeys = 1; | |||
| 92 | + | } | |||
| 93 | + | ||||
| 94 | + | // RemovePasskeyRequest names the passkey to remove. | |||
| 95 | + | message RemovePasskeyRequest { | |||
| 96 | + | // Id is the credential id from Passkey.id. | |||
| 97 | + | string id = 1; | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | // RemovePasskeyResponse is empty. | |||
| 101 | + | message RemovePasskeyResponse {} | |||