feat(session): add passkey list and remove RPCs
- commit
- 19d3b270c8835632bcfe5edb0376aa1ecf07733b
- author
- brice · · 2026-10-12 14:47 UTC
- parent
- 97a9365
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
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
| @@ -192,6 +192,259 @@ func (*SignOutEverywhereResponse) Descriptor() ([]byte, []int) { | |||
| 192 | 192 | return file_ermite_session_proto_rawDescGZIP(), []int{3} | |
| 193 | 193 | } | |
| 194 | 194 | ||
| 195 | + | // Passkey is a WebAuthn credential that can sign the owner in. It never | |
| 196 | + | // carries key material. | |
| 197 | + | type Passkey struct { | |
| 198 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 199 | + | // Id is the credential id, base64url without padding. Pass it to | |
| 200 | + | // RemovePasskey. | |
| 201 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |
| 202 | + | // Name is the label the owner gave the passkey when registering it. | |
| 203 | + | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | |
| 204 | + | // CreatedAt is when the passkey was registered. | |
| 205 | + | CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | |
| 206 | + | // LastUsedAt is when the passkey last signed the owner in. It is unset | |
| 207 | + | // when the passkey was never used. | |
| 208 | + | LastUsedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_used_at,json=lastUsedAt,proto3" json:"last_used_at,omitempty"` | |
| 209 | + | // Synced is true when the authenticator reports the credential as backed | |
| 210 | + | // up (the backup state flag), for example a passkey synced by a platform or | |
| 211 | + | // a password manager. | |
| 212 | + | Synced bool `protobuf:"varint,5,opt,name=synced,proto3" json:"synced,omitempty"` | |
| 213 | + | unknownFields protoimpl.UnknownFields | |
| 214 | + | sizeCache protoimpl.SizeCache | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | func (x *Passkey) Reset() { | |
| 218 | + | *x = Passkey{} | |
| 219 | + | mi := &file_ermite_session_proto_msgTypes[4] | |
| 220 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 221 | + | ms.StoreMessageInfo(mi) | |
| 222 | + | } | |
| 223 | + | ||
| 224 | + | func (x *Passkey) String() string { | |
| 225 | + | return protoimpl.X.MessageStringOf(x) | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | func (*Passkey) ProtoMessage() {} | |
| 229 | + | ||
| 230 | + | func (x *Passkey) ProtoReflect() protoreflect.Message { | |
| 231 | + | mi := &file_ermite_session_proto_msgTypes[4] | |
| 232 | + | if x != nil { | |
| 233 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 234 | + | if ms.LoadMessageInfo() == nil { | |
| 235 | + | ms.StoreMessageInfo(mi) | |
| 236 | + | } | |
| 237 | + | return ms | |
| 238 | + | } | |
| 239 | + | return mi.MessageOf(x) | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | // Deprecated: Use Passkey.ProtoReflect.Descriptor instead. | |
| 243 | + | func (*Passkey) Descriptor() ([]byte, []int) { | |
| 244 | + | return file_ermite_session_proto_rawDescGZIP(), []int{4} | |
| 245 | + | } | |
| 246 | + | ||
| 247 | + | func (x *Passkey) GetId() string { | |
| 248 | + | if x != nil { | |
| 249 | + | return x.Id | |
| 250 | + | } | |
| 251 | + | return "" | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | func (x *Passkey) GetName() string { | |
| 255 | + | if x != nil { | |
| 256 | + | return x.Name | |
| 257 | + | } | |
| 258 | + | return "" | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | func (x *Passkey) GetCreatedAt() *timestamppb.Timestamp { | |
| 262 | + | if x != nil { | |
| 263 | + | return x.CreatedAt | |
| 264 | + | } | |
| 265 | + | return nil | |
| 266 | + | } | |
| 267 | + | ||
| 268 | + | func (x *Passkey) GetLastUsedAt() *timestamppb.Timestamp { | |
| 269 | + | if x != nil { | |
| 270 | + | return x.LastUsedAt | |
| 271 | + | } | |
| 272 | + | return nil | |
| 273 | + | } | |
| 274 | + | ||
| 275 | + | func (x *Passkey) GetSynced() bool { | |
| 276 | + | if x != nil { | |
| 277 | + | return x.Synced | |
| 278 | + | } | |
| 279 | + | return false | |
| 280 | + | } | |
| 281 | + | ||
| 282 | + | // ListPasskeysRequest is empty. | |
| 283 | + | type ListPasskeysRequest struct { | |
| 284 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 285 | + | unknownFields protoimpl.UnknownFields | |
| 286 | + | sizeCache protoimpl.SizeCache | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | func (x *ListPasskeysRequest) Reset() { | |
| 290 | + | *x = ListPasskeysRequest{} | |
| 291 | + | mi := &file_ermite_session_proto_msgTypes[5] | |
| 292 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 293 | + | ms.StoreMessageInfo(mi) | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | func (x *ListPasskeysRequest) String() string { | |
| 297 | + | return protoimpl.X.MessageStringOf(x) | |
| 298 | + | } | |
| 299 | + | ||
| 300 | + | func (*ListPasskeysRequest) ProtoMessage() {} | |
| 301 | + | ||
| 302 | + | func (x *ListPasskeysRequest) ProtoReflect() protoreflect.Message { | |
| 303 | + | mi := &file_ermite_session_proto_msgTypes[5] | |
| 304 | + | if x != nil { | |
| 305 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 306 | + | if ms.LoadMessageInfo() == nil { | |
| 307 | + | ms.StoreMessageInfo(mi) | |
| 308 | + | } | |
| 309 | + | return ms | |
| 310 | + | } | |
| 311 | + | return mi.MessageOf(x) | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | // Deprecated: Use ListPasskeysRequest.ProtoReflect.Descriptor instead. | |
| 315 | + | func (*ListPasskeysRequest) Descriptor() ([]byte, []int) { | |
| 316 | + | return file_ermite_session_proto_rawDescGZIP(), []int{5} | |
| 317 | + | } | |
| 318 | + | ||
| 319 | + | // ListPasskeysResponse holds the passkeys. | |
| 320 | + | type ListPasskeysResponse struct { | |
| 321 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 322 | + | // Passkeys are the registered passkeys, oldest first. | |
| 323 | + | Passkeys []*Passkey `protobuf:"bytes,1,rep,name=passkeys,proto3" json:"passkeys,omitempty"` | |
| 324 | + | unknownFields protoimpl.UnknownFields | |
| 325 | + | sizeCache protoimpl.SizeCache | |
| 326 | + | } | |
| 327 | + | ||
| 328 | + | func (x *ListPasskeysResponse) Reset() { | |
| 329 | + | *x = ListPasskeysResponse{} | |
| 330 | + | mi := &file_ermite_session_proto_msgTypes[6] | |
| 331 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 332 | + | ms.StoreMessageInfo(mi) | |
| 333 | + | } | |
| 334 | + | ||
| 335 | + | func (x *ListPasskeysResponse) String() string { | |
| 336 | + | return protoimpl.X.MessageStringOf(x) | |
| 337 | + | } | |
| 338 | + | ||
| 339 | + | func (*ListPasskeysResponse) ProtoMessage() {} | |
| 340 | + | ||
| 341 | + | func (x *ListPasskeysResponse) ProtoReflect() protoreflect.Message { | |
| 342 | + | mi := &file_ermite_session_proto_msgTypes[6] | |
| 343 | + | if x != nil { | |
| 344 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 345 | + | if ms.LoadMessageInfo() == nil { | |
| 346 | + | ms.StoreMessageInfo(mi) | |
| 347 | + | } | |
| 348 | + | return ms | |
| 349 | + | } | |
| 350 | + | return mi.MessageOf(x) | |
| 351 | + | } | |
| 352 | + | ||
| 353 | + | // Deprecated: Use ListPasskeysResponse.ProtoReflect.Descriptor instead. | |
| 354 | + | func (*ListPasskeysResponse) Descriptor() ([]byte, []int) { | |
| 355 | + | return file_ermite_session_proto_rawDescGZIP(), []int{6} | |
| 356 | + | } | |
| 357 | + | ||
| 358 | + | func (x *ListPasskeysResponse) GetPasskeys() []*Passkey { | |
| 359 | + | if x != nil { | |
| 360 | + | return x.Passkeys | |
| 361 | + | } | |
| 362 | + | return nil | |
| 363 | + | } | |
| 364 | + | ||
| 365 | + | // RemovePasskeyRequest names the passkey to remove. | |
| 366 | + | type RemovePasskeyRequest struct { | |
| 367 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 368 | + | // Id is the credential id from Passkey.id. | |
| 369 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |
| 370 | + | unknownFields protoimpl.UnknownFields | |
| 371 | + | sizeCache protoimpl.SizeCache | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | func (x *RemovePasskeyRequest) Reset() { | |
| 375 | + | *x = RemovePasskeyRequest{} | |
| 376 | + | mi := &file_ermite_session_proto_msgTypes[7] | |
| 377 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 378 | + | ms.StoreMessageInfo(mi) | |
| 379 | + | } | |
| 380 | + | ||
| 381 | + | func (x *RemovePasskeyRequest) String() string { | |
| 382 | + | return protoimpl.X.MessageStringOf(x) | |
| 383 | + | } | |
| 384 | + | ||
| 385 | + | func (*RemovePasskeyRequest) ProtoMessage() {} | |
| 386 | + | ||
| 387 | + | func (x *RemovePasskeyRequest) ProtoReflect() protoreflect.Message { | |
| 388 | + | mi := &file_ermite_session_proto_msgTypes[7] | |
| 389 | + | if x != nil { | |
| 390 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 391 | + | if ms.LoadMessageInfo() == nil { | |
| 392 | + | ms.StoreMessageInfo(mi) | |
| 393 | + | } | |
| 394 | + | return ms | |
| 395 | + | } | |
| 396 | + | return mi.MessageOf(x) | |
| 397 | + | } | |
| 398 | + | ||
| 399 | + | // Deprecated: Use RemovePasskeyRequest.ProtoReflect.Descriptor instead. | |
| 400 | + | func (*RemovePasskeyRequest) Descriptor() ([]byte, []int) { | |
| 401 | + | return file_ermite_session_proto_rawDescGZIP(), []int{7} | |
| 402 | + | } | |
| 403 | + | ||
| 404 | + | func (x *RemovePasskeyRequest) GetId() string { | |
| 405 | + | if x != nil { | |
| 406 | + | return x.Id | |
| 407 | + | } | |
| 408 | + | return "" | |
| 409 | + | } | |
| 410 | + | ||
| 411 | + | // RemovePasskeyResponse is empty. | |
| 412 | + | type RemovePasskeyResponse struct { | |
| 413 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 414 | + | unknownFields protoimpl.UnknownFields | |
| 415 | + | sizeCache protoimpl.SizeCache | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | func (x *RemovePasskeyResponse) Reset() { | |
| 419 | + | *x = RemovePasskeyResponse{} | |
| 420 | + | mi := &file_ermite_session_proto_msgTypes[8] | |
| 421 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 422 | + | ms.StoreMessageInfo(mi) | |
| 423 | + | } | |
| 424 | + | ||
| 425 | + | func (x *RemovePasskeyResponse) String() string { | |
| 426 | + | return protoimpl.X.MessageStringOf(x) | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | func (*RemovePasskeyResponse) ProtoMessage() {} | |
| 430 | + | ||
| 431 | + | func (x *RemovePasskeyResponse) ProtoReflect() protoreflect.Message { | |
| 432 | + | mi := &file_ermite_session_proto_msgTypes[8] | |
| 433 | + | if x != nil { | |
| 434 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 435 | + | if ms.LoadMessageInfo() == nil { | |
| 436 | + | ms.StoreMessageInfo(mi) | |
| 437 | + | } | |
| 438 | + | return ms | |
| 439 | + | } | |
| 440 | + | return mi.MessageOf(x) | |
| 441 | + | } | |
| 442 | + | ||
| 443 | + | // Deprecated: Use RemovePasskeyResponse.ProtoReflect.Descriptor instead. | |
| 444 | + | func (*RemovePasskeyResponse) Descriptor() ([]byte, []int) { | |
| 445 | + | return file_ermite_session_proto_rawDescGZIP(), []int{8} | |
| 446 | + | } | |
| 447 | + | ||
| 195 | 448 | var File_ermite_session_proto protoreflect.FileDescriptor | |
| 196 | 449 | ||
| 197 | 450 | const file_ermite_session_proto_rawDesc = "" + | |
| @@ -203,10 +456,26 @@ const file_ermite_session_proto_rawDesc = "" + | |||
| 203 | 456 | "\n" + | |
| 204 | 457 | "expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\x1a\n" + | |
| 205 | 458 | "\x18SignOutEverywhereRequest\"\x1b\n" + | |
| 206 | - | "\x19SignOutEverywhereResponse2\xbe\x01\n" + | |
| 459 | + | "\x19SignOutEverywhereResponse\"\xbe\x01\n" + | |
| 460 | + | "\aPasskey\x12\x0e\n" + | |
| 461 | + | "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + | |
| 462 | + | "\x04name\x18\x02 \x01(\tR\x04name\x129\n" + | |
| 463 | + | "\n" + | |
| 464 | + | "created_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12<\n" + | |
| 465 | + | "\flast_used_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\n" + | |
| 466 | + | "lastUsedAt\x12\x16\n" + | |
| 467 | + | "\x06synced\x18\x05 \x01(\bR\x06synced\"\x15\n" + | |
| 468 | + | "\x13ListPasskeysRequest\"C\n" + | |
| 469 | + | "\x14ListPasskeysResponse\x12+\n" + | |
| 470 | + | "\bpasskeys\x18\x01 \x03(\v2\x0f.ermite.PasskeyR\bpasskeys\"&\n" + | |
| 471 | + | "\x14RemovePasskeyRequest\x12\x0e\n" + | |
| 472 | + | "\x02id\x18\x01 \x01(\tR\x02id\"\x17\n" + | |
| 473 | + | "\x15RemovePasskeyResponse2\xd7\x02\n" + | |
| 207 | 474 | "\x0eSessionService\x12R\n" + | |
| 208 | 475 | "\x0fCreateLoginLink\x12\x1e.ermite.CreateLoginLinkRequest\x1a\x1f.ermite.CreateLoginLinkResponse\x12X\n" + | |
| 209 | - | "\x11SignOutEverywhere\x12 .ermite.SignOutEverywhereRequest\x1a!.ermite.SignOutEverywhereResponseBs\n" + | |
| 476 | + | "\x11SignOutEverywhere\x12 .ermite.SignOutEverywhereRequest\x1a!.ermite.SignOutEverywhereResponse\x12I\n" + | |
| 477 | + | "\fListPasskeys\x12\x1b.ermite.ListPasskeysRequest\x1a\x1c.ermite.ListPasskeysResponse\x12L\n" + | |
| 478 | + | "\rRemovePasskey\x12\x1c.ermite.RemovePasskeyRequest\x1a\x1d.ermite.RemovePasskeyResponseBs\n" + | |
| 210 | 479 | "\n" + | |
| 211 | 480 | "com.ermiteB\fSessionProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3" | |
| 212 | 481 | ||
| @@ -222,25 +491,37 @@ func file_ermite_session_proto_rawDescGZIP() []byte { | |||
| 222 | 491 | return file_ermite_session_proto_rawDescData | |
| 223 | 492 | } | |
| 224 | 493 | ||
| 225 | - | var file_ermite_session_proto_msgTypes = make([]protoimpl.MessageInfo, 4) | |
| 494 | + | var file_ermite_session_proto_msgTypes = make([]protoimpl.MessageInfo, 9) | |
| 226 | 495 | var file_ermite_session_proto_goTypes = []any{ | |
| 227 | 496 | (*CreateLoginLinkRequest)(nil), // 0: ermite.CreateLoginLinkRequest | |
| 228 | 497 | (*CreateLoginLinkResponse)(nil), // 1: ermite.CreateLoginLinkResponse | |
| 229 | 498 | (*SignOutEverywhereRequest)(nil), // 2: ermite.SignOutEverywhereRequest | |
| 230 | 499 | (*SignOutEverywhereResponse)(nil), // 3: ermite.SignOutEverywhereResponse | |
| 231 | - | (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp | |
| 500 | + | (*Passkey)(nil), // 4: ermite.Passkey | |
| 501 | + | (*ListPasskeysRequest)(nil), // 5: ermite.ListPasskeysRequest | |
| 502 | + | (*ListPasskeysResponse)(nil), // 6: ermite.ListPasskeysResponse | |
| 503 | + | (*RemovePasskeyRequest)(nil), // 7: ermite.RemovePasskeyRequest | |
| 504 | + | (*RemovePasskeyResponse)(nil), // 8: ermite.RemovePasskeyResponse | |
| 505 | + | (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp | |
| 232 | 506 | } | |
| 233 | 507 | var file_ermite_session_proto_depIdxs = []int32{ | |
| 234 | - | 4, // 0: ermite.CreateLoginLinkResponse.expires_at:type_name -> google.protobuf.Timestamp | |
| 235 | - | 0, // 1: ermite.SessionService.CreateLoginLink:input_type -> ermite.CreateLoginLinkRequest | |
| 236 | - | 2, // 2: ermite.SessionService.SignOutEverywhere:input_type -> ermite.SignOutEverywhereRequest | |
| 237 | - | 1, // 3: ermite.SessionService.CreateLoginLink:output_type -> ermite.CreateLoginLinkResponse | |
| 238 | - | 3, // 4: ermite.SessionService.SignOutEverywhere:output_type -> ermite.SignOutEverywhereResponse | |
| 239 | - | 3, // [3:5] is the sub-list for method output_type | |
| 240 | - | 1, // [1:3] is the sub-list for method input_type | |
| 241 | - | 1, // [1:1] is the sub-list for extension type_name | |
| 242 | - | 1, // [1:1] is the sub-list for extension extendee | |
| 243 | - | 0, // [0:1] is the sub-list for field type_name | |
| 508 | + | 9, // 0: ermite.CreateLoginLinkResponse.expires_at:type_name -> google.protobuf.Timestamp | |
| 509 | + | 9, // 1: ermite.Passkey.created_at:type_name -> google.protobuf.Timestamp | |
| 510 | + | 9, // 2: ermite.Passkey.last_used_at:type_name -> google.protobuf.Timestamp | |
| 511 | + | 4, // 3: ermite.ListPasskeysResponse.passkeys:type_name -> ermite.Passkey | |
| 512 | + | 0, // 4: ermite.SessionService.CreateLoginLink:input_type -> ermite.CreateLoginLinkRequest | |
| 513 | + | 2, // 5: ermite.SessionService.SignOutEverywhere:input_type -> ermite.SignOutEverywhereRequest | |
| 514 | + | 5, // 6: ermite.SessionService.ListPasskeys:input_type -> ermite.ListPasskeysRequest | |
| 515 | + | 7, // 7: ermite.SessionService.RemovePasskey:input_type -> ermite.RemovePasskeyRequest | |
| 516 | + | 1, // 8: ermite.SessionService.CreateLoginLink:output_type -> ermite.CreateLoginLinkResponse | |
| 517 | + | 3, // 9: ermite.SessionService.SignOutEverywhere:output_type -> ermite.SignOutEverywhereResponse | |
| 518 | + | 6, // 10: ermite.SessionService.ListPasskeys:output_type -> ermite.ListPasskeysResponse | |
| 519 | + | 8, // 11: ermite.SessionService.RemovePasskey:output_type -> ermite.RemovePasskeyResponse | |
| 520 | + | 8, // [8:12] is the sub-list for method output_type | |
| 521 | + | 4, // [4:8] is the sub-list for method input_type | |
| 522 | + | 4, // [4:4] is the sub-list for extension type_name | |
| 523 | + | 4, // [4:4] is the sub-list for extension extendee | |
| 524 | + | 0, // [0:4] is the sub-list for field type_name | |
| 244 | 525 | } | |
| 245 | 526 | ||
| 246 | 527 | func init() { file_ermite_session_proto_init() } | |
| @@ -254,7 +535,7 @@ func file_ermite_session_proto_init() { | |||
| 254 | 535 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | |
| 255 | 536 | RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_session_proto_rawDesc), len(file_ermite_session_proto_rawDesc)), | |
| 256 | 537 | NumEnums: 0, | |
| 257 | - | NumMessages: 4, | |
| 538 | + | NumMessages: 9, | |
| 258 | 539 | NumExtensions: 0, | |
| 259 | 540 | NumServices: 1, | |
| 260 | 541 | }, | |
| @@ -192,6 +192,259 @@ func (*SignOutEverywhereResponse) Descriptor() ([]byte, []int) { | |||||
| 192 | return file_ermite_session_proto_rawDescGZIP(), []int{3} | 192 | return file_ermite_session_proto_rawDescGZIP(), []int{3} | ||
| 193 | } | 193 | } | ||
| 194 | 194 | ||||
| 195 | + | // Passkey is a WebAuthn credential that can sign the owner in. It never | |||
| 196 | + | // carries key material. | |||
| 197 | + | type Passkey struct { | |||
| 198 | + | state protoimpl.MessageState `protogen:"open.v1"` | |||
| 199 | + | // Id is the credential id, base64url without padding. Pass it to | |||
| 200 | + | // RemovePasskey. | |||
| 201 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |||
| 202 | + | // Name is the label the owner gave the passkey when registering it. | |||
| 203 | + | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | |||
| 204 | + | // CreatedAt is when the passkey was registered. | |||
| 205 | + | CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | |||
| 206 | + | // LastUsedAt is when the passkey last signed the owner in. It is unset | |||
| 207 | + | // when the passkey was never used. | |||
| 208 | + | LastUsedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_used_at,json=lastUsedAt,proto3" json:"last_used_at,omitempty"` | |||
| 209 | + | // Synced is true when the authenticator reports the credential as backed | |||
| 210 | + | // up (the backup state flag), for example a passkey synced by a platform or | |||
| 211 | + | // a password manager. | |||
| 212 | + | Synced bool `protobuf:"varint,5,opt,name=synced,proto3" json:"synced,omitempty"` | |||
| 213 | + | unknownFields protoimpl.UnknownFields | |||
| 214 | + | sizeCache protoimpl.SizeCache | |||
| 215 | + | } | |||
| 216 | + | ||||
| 217 | + | func (x *Passkey) Reset() { | |||
| 218 | + | *x = Passkey{} | |||
| 219 | + | mi := &file_ermite_session_proto_msgTypes[4] | |||
| 220 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 221 | + | ms.StoreMessageInfo(mi) | |||
| 222 | + | } | |||
| 223 | + | ||||
| 224 | + | func (x *Passkey) String() string { | |||
| 225 | + | return protoimpl.X.MessageStringOf(x) | |||
| 226 | + | } | |||
| 227 | + | ||||
| 228 | + | func (*Passkey) ProtoMessage() {} | |||
| 229 | + | ||||
| 230 | + | func (x *Passkey) ProtoReflect() protoreflect.Message { | |||
| 231 | + | mi := &file_ermite_session_proto_msgTypes[4] | |||
| 232 | + | if x != nil { | |||
| 233 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 234 | + | if ms.LoadMessageInfo() == nil { | |||
| 235 | + | ms.StoreMessageInfo(mi) | |||
| 236 | + | } | |||
| 237 | + | return ms | |||
| 238 | + | } | |||
| 239 | + | return mi.MessageOf(x) | |||
| 240 | + | } | |||
| 241 | + | ||||
| 242 | + | // Deprecated: Use Passkey.ProtoReflect.Descriptor instead. | |||
| 243 | + | func (*Passkey) Descriptor() ([]byte, []int) { | |||
| 244 | + | return file_ermite_session_proto_rawDescGZIP(), []int{4} | |||
| 245 | + | } | |||
| 246 | + | ||||
| 247 | + | func (x *Passkey) GetId() string { | |||
| 248 | + | if x != nil { | |||
| 249 | + | return x.Id | |||
| 250 | + | } | |||
| 251 | + | return "" | |||
| 252 | + | } | |||
| 253 | + | ||||
| 254 | + | func (x *Passkey) GetName() string { | |||
| 255 | + | if x != nil { | |||
| 256 | + | return x.Name | |||
| 257 | + | } | |||
| 258 | + | return "" | |||
| 259 | + | } | |||
| 260 | + | ||||
| 261 | + | func (x *Passkey) GetCreatedAt() *timestamppb.Timestamp { | |||
| 262 | + | if x != nil { | |||
| 263 | + | return x.CreatedAt | |||
| 264 | + | } | |||
| 265 | + | return nil | |||
| 266 | + | } | |||
| 267 | + | ||||
| 268 | + | func (x *Passkey) GetLastUsedAt() *timestamppb.Timestamp { | |||
| 269 | + | if x != nil { | |||
| 270 | + | return x.LastUsedAt | |||
| 271 | + | } | |||
| 272 | + | return nil | |||
| 273 | + | } | |||
| 274 | + | ||||
| 275 | + | func (x *Passkey) GetSynced() bool { | |||
| 276 | + | if x != nil { | |||
| 277 | + | return x.Synced | |||
| 278 | + | } | |||
| 279 | + | return false | |||
| 280 | + | } | |||
| 281 | + | ||||
| 282 | + | // ListPasskeysRequest is empty. | |||
| 283 | + | type ListPasskeysRequest struct { | |||
| 284 | + | state protoimpl.MessageState `protogen:"open.v1"` | |||
| 285 | + | unknownFields protoimpl.UnknownFields | |||
| 286 | + | sizeCache protoimpl.SizeCache | |||
| 287 | + | } | |||
| 288 | + | ||||
| 289 | + | func (x *ListPasskeysRequest) Reset() { | |||
| 290 | + | *x = ListPasskeysRequest{} | |||
| 291 | + | mi := &file_ermite_session_proto_msgTypes[5] | |||
| 292 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 293 | + | ms.StoreMessageInfo(mi) | |||
| 294 | + | } | |||
| 295 | + | ||||
| 296 | + | func (x *ListPasskeysRequest) String() string { | |||
| 297 | + | return protoimpl.X.MessageStringOf(x) | |||
| 298 | + | } | |||
| 299 | + | ||||
| 300 | + | func (*ListPasskeysRequest) ProtoMessage() {} | |||
| 301 | + | ||||
| 302 | + | func (x *ListPasskeysRequest) ProtoReflect() protoreflect.Message { | |||
| 303 | + | mi := &file_ermite_session_proto_msgTypes[5] | |||
| 304 | + | if x != nil { | |||
| 305 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 306 | + | if ms.LoadMessageInfo() == nil { | |||
| 307 | + | ms.StoreMessageInfo(mi) | |||
| 308 | + | } | |||
| 309 | + | return ms | |||
| 310 | + | } | |||
| 311 | + | return mi.MessageOf(x) | |||
| 312 | + | } | |||
| 313 | + | ||||
| 314 | + | // Deprecated: Use ListPasskeysRequest.ProtoReflect.Descriptor instead. | |||
| 315 | + | func (*ListPasskeysRequest) Descriptor() ([]byte, []int) { | |||
| 316 | + | return file_ermite_session_proto_rawDescGZIP(), []int{5} | |||
| 317 | + | } | |||
| 318 | + | ||||
| 319 | + | // ListPasskeysResponse holds the passkeys. | |||
| 320 | + | type ListPasskeysResponse struct { | |||
| 321 | + | state protoimpl.MessageState `protogen:"open.v1"` | |||
| 322 | + | // Passkeys are the registered passkeys, oldest first. | |||
| 323 | + | Passkeys []*Passkey `protobuf:"bytes,1,rep,name=passkeys,proto3" json:"passkeys,omitempty"` | |||
| 324 | + | unknownFields protoimpl.UnknownFields | |||
| 325 | + | sizeCache protoimpl.SizeCache | |||
| 326 | + | } | |||
| 327 | + | ||||
| 328 | + | func (x *ListPasskeysResponse) Reset() { | |||
| 329 | + | *x = ListPasskeysResponse{} | |||
| 330 | + | mi := &file_ermite_session_proto_msgTypes[6] | |||
| 331 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 332 | + | ms.StoreMessageInfo(mi) | |||
| 333 | + | } | |||
| 334 | + | ||||
| 335 | + | func (x *ListPasskeysResponse) String() string { | |||
| 336 | + | return protoimpl.X.MessageStringOf(x) | |||
| 337 | + | } | |||
| 338 | + | ||||
| 339 | + | func (*ListPasskeysResponse) ProtoMessage() {} | |||
| 340 | + | ||||
| 341 | + | func (x *ListPasskeysResponse) ProtoReflect() protoreflect.Message { | |||
| 342 | + | mi := &file_ermite_session_proto_msgTypes[6] | |||
| 343 | + | if x != nil { | |||
| 344 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 345 | + | if ms.LoadMessageInfo() == nil { | |||
| 346 | + | ms.StoreMessageInfo(mi) | |||
| 347 | + | } | |||
| 348 | + | return ms | |||
| 349 | + | } | |||
| 350 | + | return mi.MessageOf(x) | |||
| 351 | + | } | |||
| 352 | + | ||||
| 353 | + | // Deprecated: Use ListPasskeysResponse.ProtoReflect.Descriptor instead. | |||
| 354 | + | func (*ListPasskeysResponse) Descriptor() ([]byte, []int) { | |||
| 355 | + | return file_ermite_session_proto_rawDescGZIP(), []int{6} | |||
| 356 | + | } | |||
| 357 | + | ||||
| 358 | + | func (x *ListPasskeysResponse) GetPasskeys() []*Passkey { | |||
| 359 | + | if x != nil { | |||
| 360 | + | return x.Passkeys | |||
| 361 | + | } | |||
| 362 | + | return nil | |||
| 363 | + | } | |||
| 364 | + | ||||
| 365 | + | // RemovePasskeyRequest names the passkey to remove. | |||
| 366 | + | type RemovePasskeyRequest struct { | |||
| 367 | + | state protoimpl.MessageState `protogen:"open.v1"` | |||
| 368 | + | // Id is the credential id from Passkey.id. | |||
| 369 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |||
| 370 | + | unknownFields protoimpl.UnknownFields | |||
| 371 | + | sizeCache protoimpl.SizeCache | |||
| 372 | + | } | |||
| 373 | + | ||||
| 374 | + | func (x *RemovePasskeyRequest) Reset() { | |||
| 375 | + | *x = RemovePasskeyRequest{} | |||
| 376 | + | mi := &file_ermite_session_proto_msgTypes[7] | |||
| 377 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 378 | + | ms.StoreMessageInfo(mi) | |||
| 379 | + | } | |||
| 380 | + | ||||
| 381 | + | func (x *RemovePasskeyRequest) String() string { | |||
| 382 | + | return protoimpl.X.MessageStringOf(x) | |||
| 383 | + | } | |||
| 384 | + | ||||
| 385 | + | func (*RemovePasskeyRequest) ProtoMessage() {} | |||
| 386 | + | ||||
| 387 | + | func (x *RemovePasskeyRequest) ProtoReflect() protoreflect.Message { | |||
| 388 | + | mi := &file_ermite_session_proto_msgTypes[7] | |||
| 389 | + | if x != nil { | |||
| 390 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 391 | + | if ms.LoadMessageInfo() == nil { | |||
| 392 | + | ms.StoreMessageInfo(mi) | |||
| 393 | + | } | |||
| 394 | + | return ms | |||
| 395 | + | } | |||
| 396 | + | return mi.MessageOf(x) | |||
| 397 | + | } | |||
| 398 | + | ||||
| 399 | + | // Deprecated: Use RemovePasskeyRequest.ProtoReflect.Descriptor instead. | |||
| 400 | + | func (*RemovePasskeyRequest) Descriptor() ([]byte, []int) { | |||
| 401 | + | return file_ermite_session_proto_rawDescGZIP(), []int{7} | |||
| 402 | + | } | |||
| 403 | + | ||||
| 404 | + | func (x *RemovePasskeyRequest) GetId() string { | |||
| 405 | + | if x != nil { | |||
| 406 | + | return x.Id | |||
| 407 | + | } | |||
| 408 | + | return "" | |||
| 409 | + | } | |||
| 410 | + | ||||
| 411 | + | // RemovePasskeyResponse is empty. | |||
| 412 | + | type RemovePasskeyResponse struct { | |||
| 413 | + | state protoimpl.MessageState `protogen:"open.v1"` | |||
| 414 | + | unknownFields protoimpl.UnknownFields | |||
| 415 | + | sizeCache protoimpl.SizeCache | |||
| 416 | + | } | |||
| 417 | + | ||||
| 418 | + | func (x *RemovePasskeyResponse) Reset() { | |||
| 419 | + | *x = RemovePasskeyResponse{} | |||
| 420 | + | mi := &file_ermite_session_proto_msgTypes[8] | |||
| 421 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 422 | + | ms.StoreMessageInfo(mi) | |||
| 423 | + | } | |||
| 424 | + | ||||
| 425 | + | func (x *RemovePasskeyResponse) String() string { | |||
| 426 | + | return protoimpl.X.MessageStringOf(x) | |||
| 427 | + | } | |||
| 428 | + | ||||
| 429 | + | func (*RemovePasskeyResponse) ProtoMessage() {} | |||
| 430 | + | ||||
| 431 | + | func (x *RemovePasskeyResponse) ProtoReflect() protoreflect.Message { | |||
| 432 | + | mi := &file_ermite_session_proto_msgTypes[8] | |||
| 433 | + | if x != nil { | |||
| 434 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| 435 | + | if ms.LoadMessageInfo() == nil { | |||
| 436 | + | ms.StoreMessageInfo(mi) | |||
| 437 | + | } | |||
| 438 | + | return ms | |||
| 439 | + | } | |||
| 440 | + | return mi.MessageOf(x) | |||
| 441 | + | } | |||
| 442 | + | ||||
| 443 | + | // Deprecated: Use RemovePasskeyResponse.ProtoReflect.Descriptor instead. | |||
| 444 | + | func (*RemovePasskeyResponse) Descriptor() ([]byte, []int) { | |||
| 445 | + | return file_ermite_session_proto_rawDescGZIP(), []int{8} | |||
| 446 | + | } | |||
| 447 | + | ||||
| 195 | var File_ermite_session_proto protoreflect.FileDescriptor | 448 | var File_ermite_session_proto protoreflect.FileDescriptor | ||
| 196 | 449 | ||||
| 197 | const file_ermite_session_proto_rawDesc = "" + | 450 | const file_ermite_session_proto_rawDesc = "" + | ||
| @@ -203,10 +456,26 @@ const file_ermite_session_proto_rawDesc = "" + | |||||
| 203 | "\n" + | 456 | "\n" + | ||
| 204 | "expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\x1a\n" + | 457 | "expires_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"\x1a\n" + | ||
| 205 | "\x18SignOutEverywhereRequest\"\x1b\n" + | 458 | "\x18SignOutEverywhereRequest\"\x1b\n" + | ||
| 206 | - | "\x19SignOutEverywhereResponse2\xbe\x01\n" + | 459 | + | "\x19SignOutEverywhereResponse\"\xbe\x01\n" + |
| 460 | + | "\aPasskey\x12\x0e\n" + | |||
| 461 | + | "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + | |||
| 462 | + | "\x04name\x18\x02 \x01(\tR\x04name\x129\n" + | |||
| 463 | + | "\n" + | |||
| 464 | + | "created_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12<\n" + | |||
| 465 | + | "\flast_used_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\n" + | |||
| 466 | + | "lastUsedAt\x12\x16\n" + | |||
| 467 | + | "\x06synced\x18\x05 \x01(\bR\x06synced\"\x15\n" + | |||
| 468 | + | "\x13ListPasskeysRequest\"C\n" + | |||
| 469 | + | "\x14ListPasskeysResponse\x12+\n" + | |||
| 470 | + | "\bpasskeys\x18\x01 \x03(\v2\x0f.ermite.PasskeyR\bpasskeys\"&\n" + | |||
| 471 | + | "\x14RemovePasskeyRequest\x12\x0e\n" + | |||
| 472 | + | "\x02id\x18\x01 \x01(\tR\x02id\"\x17\n" + | |||
| 473 | + | "\x15RemovePasskeyResponse2\xd7\x02\n" + | |||
| 207 | "\x0eSessionService\x12R\n" + | 474 | "\x0eSessionService\x12R\n" + | ||
| 208 | "\x0fCreateLoginLink\x12\x1e.ermite.CreateLoginLinkRequest\x1a\x1f.ermite.CreateLoginLinkResponse\x12X\n" + | 475 | "\x0fCreateLoginLink\x12\x1e.ermite.CreateLoginLinkRequest\x1a\x1f.ermite.CreateLoginLinkResponse\x12X\n" + | ||
| 209 | - | "\x11SignOutEverywhere\x12 .ermite.SignOutEverywhereRequest\x1a!.ermite.SignOutEverywhereResponseBs\n" + | 476 | + | "\x11SignOutEverywhere\x12 .ermite.SignOutEverywhereRequest\x1a!.ermite.SignOutEverywhereResponse\x12I\n" + |
| 477 | + | "\fListPasskeys\x12\x1b.ermite.ListPasskeysRequest\x1a\x1c.ermite.ListPasskeysResponse\x12L\n" + | |||
| 478 | + | "\rRemovePasskey\x12\x1c.ermite.RemovePasskeyRequest\x1a\x1d.ermite.RemovePasskeyResponseBs\n" + | |||
| 210 | "\n" + | 479 | "\n" + | ||
| 211 | "com.ermiteB\fSessionProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3" | 480 | "com.ermiteB\fSessionProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3" | ||
| 212 | 481 | ||||
| @@ -222,25 +491,37 @@ func file_ermite_session_proto_rawDescGZIP() []byte { | |||||
| 222 | return file_ermite_session_proto_rawDescData | 491 | return file_ermite_session_proto_rawDescData | ||
| 223 | } | 492 | } | ||
| 224 | 493 | ||||
| 225 | - | var file_ermite_session_proto_msgTypes = make([]protoimpl.MessageInfo, 4) | 494 | + | var file_ermite_session_proto_msgTypes = make([]protoimpl.MessageInfo, 9) |
| 226 | var file_ermite_session_proto_goTypes = []any{ | 495 | var file_ermite_session_proto_goTypes = []any{ | ||
| 227 | (*CreateLoginLinkRequest)(nil), // 0: ermite.CreateLoginLinkRequest | 496 | (*CreateLoginLinkRequest)(nil), // 0: ermite.CreateLoginLinkRequest | ||
| 228 | (*CreateLoginLinkResponse)(nil), // 1: ermite.CreateLoginLinkResponse | 497 | (*CreateLoginLinkResponse)(nil), // 1: ermite.CreateLoginLinkResponse | ||
| 229 | (*SignOutEverywhereRequest)(nil), // 2: ermite.SignOutEverywhereRequest | 498 | (*SignOutEverywhereRequest)(nil), // 2: ermite.SignOutEverywhereRequest | ||
| 230 | (*SignOutEverywhereResponse)(nil), // 3: ermite.SignOutEverywhereResponse | 499 | (*SignOutEverywhereResponse)(nil), // 3: ermite.SignOutEverywhereResponse | ||
| 231 | - | (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp | 500 | + | (*Passkey)(nil), // 4: ermite.Passkey |
| 501 | + | (*ListPasskeysRequest)(nil), // 5: ermite.ListPasskeysRequest | |||
| 502 | + | (*ListPasskeysResponse)(nil), // 6: ermite.ListPasskeysResponse | |||
| 503 | + | (*RemovePasskeyRequest)(nil), // 7: ermite.RemovePasskeyRequest | |||
| 504 | + | (*RemovePasskeyResponse)(nil), // 8: ermite.RemovePasskeyResponse | |||
| 505 | + | (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp | |||
| 232 | } | 506 | } | ||
| 233 | var file_ermite_session_proto_depIdxs = []int32{ | 507 | var file_ermite_session_proto_depIdxs = []int32{ | ||
| 234 | - | 4, // 0: ermite.CreateLoginLinkResponse.expires_at:type_name -> google.protobuf.Timestamp | 508 | + | 9, // 0: ermite.CreateLoginLinkResponse.expires_at:type_name -> google.protobuf.Timestamp |
| 235 | - | 0, // 1: ermite.SessionService.CreateLoginLink:input_type -> ermite.CreateLoginLinkRequest | 509 | + | 9, // 1: ermite.Passkey.created_at:type_name -> google.protobuf.Timestamp |
| 236 | - | 2, // 2: ermite.SessionService.SignOutEverywhere:input_type -> ermite.SignOutEverywhereRequest | 510 | + | 9, // 2: ermite.Passkey.last_used_at:type_name -> google.protobuf.Timestamp |
| 237 | - | 1, // 3: ermite.SessionService.CreateLoginLink:output_type -> ermite.CreateLoginLinkResponse | 511 | + | 4, // 3: ermite.ListPasskeysResponse.passkeys:type_name -> ermite.Passkey |
| 238 | - | 3, // 4: ermite.SessionService.SignOutEverywhere:output_type -> ermite.SignOutEverywhereResponse | 512 | + | 0, // 4: ermite.SessionService.CreateLoginLink:input_type -> ermite.CreateLoginLinkRequest |
| 239 | - | 3, // [3:5] is the sub-list for method output_type | 513 | + | 2, // 5: ermite.SessionService.SignOutEverywhere:input_type -> ermite.SignOutEverywhereRequest |
| 240 | - | 1, // [1:3] is the sub-list for method input_type | 514 | + | 5, // 6: ermite.SessionService.ListPasskeys:input_type -> ermite.ListPasskeysRequest |
| 241 | - | 1, // [1:1] is the sub-list for extension type_name | 515 | + | 7, // 7: ermite.SessionService.RemovePasskey:input_type -> ermite.RemovePasskeyRequest |
| 242 | - | 1, // [1:1] is the sub-list for extension extendee | 516 | + | 1, // 8: ermite.SessionService.CreateLoginLink:output_type -> ermite.CreateLoginLinkResponse |
| 243 | - | 0, // [0:1] is the sub-list for field type_name | 517 | + | 3, // 9: ermite.SessionService.SignOutEverywhere:output_type -> ermite.SignOutEverywhereResponse |
| 518 | + | 6, // 10: ermite.SessionService.ListPasskeys:output_type -> ermite.ListPasskeysResponse | |||
| 519 | + | 8, // 11: ermite.SessionService.RemovePasskey:output_type -> ermite.RemovePasskeyResponse | |||
| 520 | + | 8, // [8:12] is the sub-list for method output_type | |||
| 521 | + | 4, // [4:8] is the sub-list for method input_type | |||
| 522 | + | 4, // [4:4] is the sub-list for extension type_name | |||
| 523 | + | 4, // [4:4] is the sub-list for extension extendee | |||
| 524 | + | 0, // [0:4] is the sub-list for field type_name | |||
| 244 | } | 525 | } | ||
| 245 | 526 | ||||
| 246 | func init() { file_ermite_session_proto_init() } | 527 | func init() { file_ermite_session_proto_init() } | ||
| @@ -254,7 +535,7 @@ func file_ermite_session_proto_init() { | |||||
| 254 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | 535 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | ||
| 255 | RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_session_proto_rawDesc), len(file_ermite_session_proto_rawDesc)), | 536 | RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_session_proto_rawDesc), len(file_ermite_session_proto_rawDesc)), | ||
| 256 | NumEnums: 0, | 537 | NumEnums: 0, | ||
| 257 | - | NumMessages: 4, | 538 | + | NumMessages: 9, |
| 258 | NumExtensions: 0, | 539 | NumExtensions: 0, | ||
| 259 | NumServices: 1, | 540 | NumServices: 1, | ||
| 260 | }, | 541 | }, | ||
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 {} | |||