github.com/pachyderm/pachyderm@v1.13.4/src/client/auth/auth.proto (about) 1 syntax = "proto3"; 2 3 package auth; 4 option go_package = "github.com/pachyderm/pachyderm/src/client/auth"; 5 6 import "gogoproto/gogo.proto"; 7 import "google/protobuf/timestamp.proto"; 8 9 /* A note on users 10 * 11 * Internally, in Pachyderm, usernames are structured strings. This makes both 12 * our API and our data model more flexible (at the loss of some type safety). 13 * Basically, anywhere that Pachyderm internally stores a subject (i.e. 14 * TokenInfo) or principal (ACL, the 'admins' collection), that username will 15 * have some structured prefix. 16 * 17 * Note that externally-facing principals ({Get,Set}{Scope,ACL}, ModifyAdmins, 18 * ListAdmins) will have their own conventions 19 * 20 * The current user formats are: 21 * 1) GitHub usernames: 22 * "github:MyGitHubUsername" 23 * 2) Pachyderm robot users: 24 * "robot:robot_user_1" 25 * 3) Pachyderm pipelines: 26 * "pipeline:terasort" 27 */ 28 29 //// Activation API 30 31 // ActivateRequest mirrors AuthenticateRequest. The caller is authenticated via 32 // GitHub OAuth, and then promoted to the cluster's first Admin. Afterwards, the 33 // caller can promote other users to Admin and remove themselves 34 message ActivateRequest { 35 // If set, Pachyderm will authenticate the caller as this user. 36 // - If set to a github user (i.e. it has a 'github:' prefix or no prefix) 37 // then Pachyderm will confirm that it matches the user associated with 38 // 'github_token' 39 // - If set to a robot user (i.e. it has a 'robot:' prefix), then Pachyderm 40 // will generate a new token for the robot user; this token will be the only 41 // way to administer this cluster until more admins are added. 42 string subject = 2; 43 44 // This is the token returned by GitHub and used to authenticate the caller. 45 // When Pachyderm is deployed locally, setting this value to a given string 46 // will automatically authenticate the caller as a GitHub user whose username 47 // is that string (unless this "looks like" a GitHub access code, in which 48 // case Pachyderm does retrieve the corresponding GitHub username) 49 string github_token = 1 [(gogoproto.customname) = "GitHubToken"]; 50 51 // If set, activate auth for the root user (`pach:root`), and add this 52 // token as the initial login token. The root user will have super 53 // admin permissions on the cluster. 54 string root_token = 3; 55 } 56 57 message ActivateResponse { 58 // pach_token authenticates the caller with Pachyderm (if you want to perform 59 // Pachyderm operations after auth has been activated as themselves, you must 60 // present this token along with your regular request) 61 string pach_token = 1; 62 } 63 64 message DeactivateRequest {} 65 message DeactivateResponse {} 66 67 // IDProvider configures a single ID provider that can authenticate Pachyderm 68 // users 69 message IDProvider { 70 // Name identifies this authentication backend in Pachyderm. 71 string name = 1; 72 73 // Description is a human-readable description of this authentication 74 // backend. It's ignored by Pachyderm, but exists for the benefit of users 75 // configuring Pachyderm's auth system. 76 string description = 2; 77 78 // SAMLOptions describes a SAML-based identity provider 79 message SAMLOptions { 80 // metadata_url is the URL of the SAML ID provider's metadata service 81 // (which Pachd can query to get more info about the SAML ID provider) 82 string metadata_url = 1 [(gogoproto.customname) = "MetadataURL"]; 83 84 // metadata_xml is a direct reproduction of the ID provider's metadata. 85 // Users can set this field in the argument to SetConfig if the ID provider 86 // can't be reached from pachd (e.g. because it's on a separate network to 87 // which Pachyderm users also have access) or for testing. Exactly one of 88 // metadata_url and metadata_xml should be set in calls to SetConfig, but 89 // internally, if metadata_url is set, the result of scraping the metadata 90 // URL will be placed here in the result from GetConfig(). 91 bytes metadata_xml = 2 [(gogoproto.customname) = "MetadataXML"]; 92 93 // If this ID provider supports sending group memberships via attribute, 94 // then users can set group_attribute to the SAML attribute that indicates 95 // group mmbership, and Pachyderm will update users' group memberships when 96 // they authenticate. 97 string group_attribute = 3; 98 } 99 SAMLOptions saml = 3 [(gogoproto.customname) = "SAML"]; 100 101 // OIDCOptions describes a OIDC-based identity provider 102 message OIDCOptions { 103 string issuer = 1; 104 string client_id = 2 [(gogoproto.customname) = "ClientID"]; 105 string client_secret = 3; 106 string redirect_uri = 4 [(gogoproto.customname) = "RedirectURI"]; 107 repeated string additional_scopes = 5; 108 bool ignore_email_verified = 6; 109 } 110 OIDCOptions oidc = 5 [(gogoproto.customname) = "OIDC"]; 111 112 // GitHubOptions is an empty protobuf message whose presence in the IDProvider 113 // of an AuthConfig indicates that GitHub auth should be enabled. 114 message GitHubOptions{} 115 GitHubOptions github = 4 [(gogoproto.customname) = "GitHub"]; 116 } 117 118 // Configure Pachyderm's auth system (particularly authentication backends 119 message AuthConfig { 120 // live_config_version identifies the version of a given pachyderm cluster's 121 // current auth configuration; if a user tries to write an auth configuration 122 // where live_config_version doesn't match the version of the cluster's 123 // current config, the write will fail. This allows for safe 124 // read+modify+write config changes. 125 int64 live_config_version = 1; 126 127 // id_providers describes external ID providers that can authenticate 128 // Pachyderm users (e.g. GitHub, Okta, etc) 129 repeated IDProvider id_providers = 2 [(gogoproto.customname) = "IDProviders"]; 130 131 // saml_svc_options configures the SAML services (Assertion Consumer Service 132 // and Metadata Service) that Pachd can export. 133 message SAMLServiceOptions { 134 // acs is the URL of Pachd's Assertion Consumer Service (i.e. where SAML ID 135 // providers can send SAMLResponses to Pachd). If Pachyderm is running in a 136 // private cluster, the cluster admin would be responsible for setting up a 137 // domain name/proxy to resolve to pachd:654/acs 138 string acs_url = 1 [(gogoproto.customname) = "ACSURL"]; 139 140 // metadata_url is the public URL of Pachd's SAML metadata service (some 141 // SAML ID providers will query this for information about Pachyderm's SAML 142 // implementation and use it to idenfity Pachyderm as a service provider). 143 // If Pachyderm is running in a private cluster, the cluster admin would be 144 // responsible for creating this URL (which must resolve to 145 // pachd:654/saml/metadata) 146 string metadata_url = 2 [(gogoproto.customname) = "MetadataURL"]; 147 148 // dash_url is the public address of this cluster's Pachyderm 149 // dashboard, if one exists; this option determines where users will be 150 // redirected after successfully authenticating 151 string dash_url = 3 [(gogoproto.customname) = "DashURL"]; 152 153 // session_duration determines the duration of SAML-IdP-authenticated user 154 // sessions (specified as a Golang time duration, e.g. "24h" or "600m"). If 155 // unset, user sessions last 24 hours (a short default, as SAML assertions 156 // may contain group memberships that need to be refreshed) 157 string session_duration = 4; 158 159 // debug_logging determines whether pachd emits verbose logs (including 160 // SAML credentials) as it receives them, which may be helpful for 161 // debugging. This will probably not be present in any official releases. 162 bool debug_logging = 5; 163 } 164 SAMLServiceOptions saml_svc_options = 3 [(gogoproto.customname) = "SAMLServiceOptions"]; 165 } 166 167 message GetConfigurationRequest {} 168 message GetConfigurationResponse { 169 AuthConfig configuration = 1; 170 } 171 message SetConfigurationRequest { 172 AuthConfig configuration = 1; 173 } 174 message SetConfigurationResponse {} 175 176 enum ClusterRole { 177 UNDEFINED = 0; 178 179 // SUPER gives access to all APIs and owner on everything in PFS (previous just called admin) 180 SUPER = 1; 181 182 // FS gives Owner on all repos in PFS but not access to manage other aspects of the cluster 183 FS = 2; 184 } 185 186 // ClusterRoles reflects any cluster-wide permissions a principal has. 187 // A principal can have multiple cluster roles. 188 message ClusterRoles { 189 repeated ClusterRole roles = 1; 190 } 191 192 // Get the current set of principals and roles for the cluster 193 message GetClusterRoleBindingsRequest{} 194 message GetClusterRoleBindingsResponse{ 195 // bindings contains a mapping of principals to cluster roles 196 map<string, ClusterRoles> bindings = 1; 197 } 198 199 // Set cluster roles for the specified principal. Setting an empty list of roles 200 // revokes any roles the principal has. 201 message ModifyClusterRoleBindingRequest { 202 string principal = 1; 203 ClusterRoles roles = 2; 204 } 205 message ModifyClusterRoleBindingResponse {} 206 207 // Deprecated. Get the list of cluster super admins. 208 message GetAdminsRequest{} 209 message GetAdminsResponse{ 210 repeated string admins = 1; 211 } 212 213 // Deprecated. Add and remove users from the set of cluster super admins. 214 message ModifyAdminsRequest { 215 repeated string add = 1; 216 repeated string remove = 2; 217 } 218 message ModifyAdminsResponse {} 219 220 //// Authentication data structures 221 222 // OTPInfo is the analogue of 'TokenInfo' for Authentication Codes (short-lived, 223 // one-time-use codes that are passed to the frontend and then exchanged for 224 // longer-lived tokens) 225 message OTPInfo { 226 // Subject (i.e. Pachyderm account) that a given OTP authenticates. This may 227 // be copied into the 'subject' field of a TokenInfo, and therefore has the 228 // same format, with the same prefixes. 229 string subject = 1; 230 231 // session_expiration indicates when the subject's session expires, a.k.a. 232 // when the Token to which this OTP converts expires (likely later than this 233 // OTP expires, but never earlier). 234 google.protobuf.Timestamp session_expiration = 2; 235 } 236 237 // TokenInfo is the 'value' of an auth token 'key' in the 'tokens' collection 238 message TokenInfo { 239 // Subject (i.e. Pachyderm account) that a given token authorizes. Prefixed 240 // with "github:" or "robot:" to distinguish the two classes of 241 // Subject in Pachyderm 242 string subject = 1; 243 244 enum TokenSource { 245 INVALID = 0; 246 AUTHENTICATE = 1; // returned by Authenticate()--non-revokeable 247 GET_TOKEN = 2; // returned by GetToken()--revokeable. 248 } 249 TokenSource source = 2; 250 } 251 252 //// Authentication API 253 254 message AuthenticateRequest { 255 // Exactly one of 'github_token', 'oidc_state', or 'one_time_password' must be set: 256 257 // This is the token returned by GitHub and used to authenticate the caller. 258 // When Pachyderm is deployed locally, setting this value to a given string 259 // will automatically authenticate the caller as a GitHub user whose username 260 // is that string (unless this "looks like" a GitHub access code, in which 261 // case Pachyderm does retrieve the corresponding GitHub username) 262 string github_token = 1 [(gogoproto.customname) = "GitHubToken"]; 263 // This is the session state that Pachyderm creates in order to keep track of 264 // information related to the current OIDC session. 265 string oidc_state = 3 [(gogoproto.customname) = "OIDCState"]; 266 267 // This is a short-lived, one-time-use password generated by Pachyderm, for 268 // the purpose of propagating authentication to new clients (e.g. from the 269 // dash to pachd) 270 string one_time_password = 2; 271 272 // This is an ID Token issued by the OIDC provider. 273 string id_token = 4; 274 } 275 276 message AuthenticateResponse { 277 // pach_token authenticates the caller with Pachyderm (if you want to perform 278 // Pachyderm operations after auth has been activated as themselves, you must 279 // present this token along with your regular request) 280 string pach_token = 1; 281 } 282 283 message WhoAmIRequest {} 284 285 message WhoAmIResponse { 286 string username = 1; 287 bool is_admin = 2; 288 int64 ttl = 3 [(gogoproto.customname) = "TTL"]; 289 ClusterRoles cluster_roles = 4; 290 } 291 292 //// Authorization data structures 293 294 // Scope (actually a "role" in canonical security nomenclature) represents a 295 // rough level of access that a principal has to a repo 296 enum Scope { 297 // To remove a user's scope from a repo, set their scope to NONE 298 NONE = 0; 299 READER = 1; 300 WRITER = 2; 301 OWNER = 3; 302 } 303 304 message ACL { 305 // principal -> scope. All principals are the default principal of a Pachyderm 306 // subject (i.e. all keys in this map are strings prefixed with either 307 // "github:" or "robot:", followed by the name of a GitHub user, all of whom 308 // are Pachyderm subjects, or a Pachyderm robot user) 309 map<string, Scope> entries = 1; 310 } 311 312 message Users { 313 map<string, bool> usernames = 1; 314 } 315 316 message Groups { 317 map<string, bool> groups = 1; 318 } 319 320 //// Authorization API 321 322 message AuthorizeRequest { 323 // repo is the object that the caller wants to access 324 string repo = 1; 325 326 // scope is the access level that the caller needs to perform an action 327 Scope scope = 2; 328 } 329 330 message AuthorizeResponse { 331 // authorized is true if the caller has at least 332 // 'AuthorizeRequest.scope'-level access to 'AuthorizeRequest.repo', and false 333 // otherwise 334 bool authorized = 1; 335 } 336 337 message GetScopeRequest { 338 // username is the principal (some of which belong to robots rather than 339 // users, but the name is preserved for now to provide compatibility with the 340 // pachyderm dash) whose access level is queried. To query the access level 341 // of a robot user, the caller must prefix username with "robot:". If 342 // 'username' has no prefix (i.e. no ":"), then it's assumed to be a github 343 // user's principal. 344 string username = 1; 345 346 // repos are the objects to which 'username's access level is being queried 347 repeated string repos = 2; 348 } 349 350 message GetScopeResponse { 351 // scopes (actually a "role"--see "Scope") are the access level that 352 // 'GetScopeRequest.username' has to each repo in 'GetScopeRequest.repos', in 353 // the same order that repos appeared in 'repos'. 354 repeated Scope scopes = 1; 355 } 356 357 message SetScopeRequest { 358 // username is the principal (some of which belong to robots rather than 359 // users, but the name is preserved for now to provide compatibility with the 360 // pachyderm dash) whose access is being granted/revoked. As with 361 // GetScopeRequest, to set the access level of a robot user, the caller must 362 // prefix username with "robot:". If 'username' has no prefix (i.e. no ":"), 363 // then it's assumed to be a github user's principal. 364 string username = 1; 365 366 // repo is the object to which access is being granted/revoked 367 string repo = 2; 368 369 // scope (actually a "role"--see "Scope") is the access level that the owner 370 // of 'principal' will now have 371 Scope scope = 3; 372 } 373 374 message SetScopeResponse {} 375 376 message GetACLRequest { 377 string repo = 1; 378 } 379 380 message ACLEntry { 381 // username is the principal posessing this level of access to this ACL's 382 // repo (despite the name, this principal may be for a human github user or a 383 // pachyderm robot) 384 string username = 1; 385 386 // scope is the level of access that the owner of 'principal' has to this 387 // ACL's repo (actually a role in typical security terminology) 388 Scope scope = 2; 389 } 390 391 // GetACLReponse contains the list of entries on a Pachyderm ACL. 392 // 393 // To avoid migration pain with the Pachyderm dash the list of user principal 394 // entries and robot principal entries are separate. This way, no prefix or 395 // other disambiguating device is needed in 'entries' to separate user 396 // principals from robot principals (which would confuse the dash). Instead, 397 // the dash can simply ignore robot principals. 398 message GetACLResponse { 399 // entries contains all [user principal] -> [role] mappings. This is separate 400 // from robot_entries to avoid migration pain the Pachyderm dashboard 401 repeated ACLEntry entries = 1; 402 403 // robot_entries contains all [robot principal] -> [role] mappings. This is 404 // separate from entries to be unambiguous (all keys are robot principals, but 405 // have no prefixes) while avoiding migration pain in the Pachyderm dashboard. 406 repeated ACLEntry robot_entries = 2; 407 } 408 409 message SetACLRequest { 410 string repo = 1; 411 repeated ACLEntry entries = 2; 412 } 413 414 message SetACLResponse {} 415 416 ////////////////////////////// 417 //// OIDC Data Structures //// 418 ////////////////////////////// 419 420 // SessionInfo stores information associated with one OIDC authentication 421 // session (i.e. a single instance of a single user logging in). Sessions are 422 // short-lived and stored in the 'oidc-authns' collection, keyed by the OIDC 423 // 'state' token (30-character CSPRNG-generated string). 'GetOIDCLogin' 424 // generates and inserts entries, then /authorization-code/callback retrieves 425 // an access token from the ID provider and uses it to retrive the caller's 426 // email and store it in 'email', and finally Authorize() returns a Pachyderm 427 // token identified with that email address as a subject in Pachyderm. 428 message SessionInfo { 429 // nonce is used by /authorization-code/callback to validate session 430 // continuity with the IdP after a user has arrived there from GetOIDCLogin(). 431 // This is a 30-character CSPRNG-generated string. 432 string nonce = 1; 433 // email contains the email adddress associated with a user in their OIDC ID 434 // provider. Currently users are identified with their email address rather 435 // than their OIDC subject identifier to make switching between OIDC ID 436 // providers easier for users, and to make user identities more easily 437 // comprehensible in Pachyderm. The OIDC spec doesn't require that users' 438 // emails be present or unique, but we think this will be preferable in 439 // practice. 440 string email = 2; 441 // conversion_err indicates whether an error was encountered while exchanging 442 // an auth code for an access token, or while obtaining a user's email (in 443 // /authorization-code/callback). Storing the error state here allows any 444 // sibling calls to Authenticate() (i.e. using the same OIDC state token) to 445 // notify their caller that an error has occurred. We avoid passing the caller 446 // any details of the error (which are logged by Pachyderm) to avoid giving 447 // information to a user who has network access to Pachyderm but not an 448 // account in the OIDC provider. 449 bool conversion_err = 3; 450 } 451 452 //// OIDC API 453 454 message GetOIDCLoginRequest { 455 } 456 457 message GetOIDCLoginResponse { 458 // The login URL generated for the OIDC object 459 string login_url = 1 [(gogoproto.customname) = "LoginURL"]; 460 string state = 2; 461 } 462 463 //// Token API (very limited -- for pipelines) 464 465 message GetAuthTokenRequest { 466 // The returned token will allow the caller to access resources as this 467 // subject 468 string subject = 1; 469 470 // ttl indicates the requested (approximate) remaining lifetime of this token, 471 // in seconds 472 int64 ttl = 2 [(gogoproto.customname) = "TTL"]; 473 } 474 475 message GetAuthTokenResponse { 476 // A canonicalized version of the subject in the request 477 string subject = 2; 478 479 // A new auth token for the user in 'GetAuthTokenRequest.Subject' token 480 string token = 1; 481 } 482 483 message ExtendAuthTokenRequest { 484 // token indicates the Pachyderm token whose TTL is being extended 485 string token = 1; 486 487 // ttl indicates the new TTL of 'token' (if it's longer than the existing TTL) 488 int64 ttl = 2 [(gogoproto.customname) = "TTL"]; 489 } 490 491 message ExtendAuthTokenResponse {} 492 493 message RevokeAuthTokenRequest { 494 string token = 1; 495 } 496 497 message RevokeAuthTokenResponse {} 498 499 message SetGroupsForUserRequest { 500 string username = 1; 501 repeated string groups = 2; 502 } 503 504 message SetGroupsForUserResponse {} 505 506 message ModifyMembersRequest { 507 string group = 1; 508 repeated string add = 2; 509 repeated string remove = 3; 510 } 511 512 message ModifyMembersResponse {} 513 514 message GetGroupsRequest { 515 string username = 1; 516 } 517 518 message GetGroupsResponse { 519 repeated string groups = 1; 520 } 521 522 message GetUsersRequest { 523 string group = 1; 524 } 525 526 message GetUsersResponse { 527 repeated string usernames = 1; 528 } 529 530 // GetOneTimePassword allows users to generate short-lived (~30s) tokens that 531 // can be passed to Authenticate() (via AuthenticateRequest.one_time_password) 532 // and exchanged for a longer-lived pachyderm token. This is more secure than 533 // GetAuthToken, which produces long-lived authorization tokens. 534 message GetOneTimePasswordRequest { 535 // If the caller is an admin, GetOneTimePassword() can return a code for 536 // any user (useful for testing). 537 // If the caller is not an admin, GetOneTimePassword() will return an 538 // authentication code for the caller if username is unset or set to the 539 // caller's username (and will return an error otherwise) 540 string subject = 1; 541 542 // ttl indicates the requested (approximate) remaining lifetime of this token, 543 // in seconds 544 int64 ttl = 2 [(gogoproto.customname) = "TTL"]; 545 } 546 547 message GetOneTimePasswordResponse { 548 // 'code' is the string that must be presented in 549 // AuthenticateRequest.one_time_password to login as 550 // GetOneTimePasswordRequest.subject 551 string code = 1; 552 553 // expiration is the time at which the token in 'code' will expire 554 google.protobuf.Timestamp otp_expiration = 2 [(gogoproto.customname) = "OTPExpiration"]; 555 } 556 557 message HashedAuthToken { 558 string hashed_token = 1; 559 TokenInfo token_info = 2; 560 google.protobuf.Timestamp expiration = 3; 561 } 562 563 // ExtractAuthTokens returns all the hashed robot tokens that have been issued. 564 // User tokens are not extracted as they can be recreated by logging in. 565 message ExtractAuthTokensRequest {} 566 567 message ExtractAuthTokensResponse { 568 repeated HashedAuthToken tokens = 1; 569 } 570 571 // RestoreAuthToken inserts a hashed token that has previously been extracted. 572 message RestoreAuthTokenRequest { 573 HashedAuthToken token = 1; 574 } 575 576 message RestoreAuthTokenResponse {} 577 578 service API { 579 // Activate/Deactivate the auth API. 'Activate' sets an initial set of admins 580 // for the Pachyderm cluster, and 'Deactivate' removes all ACLs, tokens, and 581 // admins from the Pachyderm cluster, making all data publicly accessable 582 rpc Activate(ActivateRequest) returns (ActivateResponse) {} 583 rpc Deactivate(DeactivateRequest) returns (DeactivateResponse) {} 584 585 rpc GetConfiguration(GetConfigurationRequest) returns (GetConfigurationResponse) {} 586 rpc SetConfiguration(SetConfigurationRequest) returns (SetConfigurationResponse) {} 587 588 // Deprecated. GetAdmins returns the current list of cluster super admins 589 rpc GetAdmins(GetAdminsRequest) returns (GetAdminsResponse) {} 590 // Deprecated. ModifyAdmins adds or removes super admins from the cluster 591 rpc ModifyAdmins(ModifyAdminsRequest) returns (ModifyAdminsResponse) {} 592 593 // GetClusterRoleBindings returns the current set of cluster role bindings 594 rpc GetClusterRoleBindings(GetClusterRoleBindingsRequest) returns (GetClusterRoleBindingsResponse) {} 595 // ModifyAdmin sets the list of admin roles for a principal 596 rpc ModifyClusterRoleBinding(ModifyClusterRoleBindingRequest) returns (ModifyClusterRoleBindingResponse) {} 597 598 rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {} 599 rpc Authorize(AuthorizeRequest) returns (AuthorizeResponse) {} 600 rpc WhoAmI(WhoAmIRequest) returns (WhoAmIResponse) {} 601 602 rpc GetScope(GetScopeRequest) returns (GetScopeResponse) {} 603 rpc SetScope(SetScopeRequest) returns (SetScopeResponse) {} 604 rpc GetACL(GetACLRequest) returns (GetACLResponse) {} 605 rpc SetACL(SetACLRequest) returns (SetACLResponse) {} 606 607 rpc GetOIDCLogin(GetOIDCLoginRequest) returns (GetOIDCLoginResponse) {} 608 609 rpc GetAuthToken(GetAuthTokenRequest) returns (GetAuthTokenResponse) {} 610 rpc ExtendAuthToken(ExtendAuthTokenRequest) returns (ExtendAuthTokenResponse) {} 611 rpc RevokeAuthToken(RevokeAuthTokenRequest) returns (RevokeAuthTokenResponse) {} 612 613 rpc SetGroupsForUser(SetGroupsForUserRequest) returns (SetGroupsForUserResponse) {} 614 rpc ModifyMembers(ModifyMembersRequest) returns (ModifyMembersResponse) {} 615 rpc GetGroups(GetGroupsRequest) returns (GetGroupsResponse) {} 616 rpc GetUsers(GetUsersRequest) returns (GetUsersResponse) {} 617 618 rpc GetOneTimePassword(GetOneTimePasswordRequest) returns (GetOneTimePasswordResponse) {} 619 620 rpc ExtractAuthTokens(ExtractAuthTokensRequest) returns (ExtractAuthTokensResponse) {} 621 rpc RestoreAuthToken(RestoreAuthTokenRequest) returns (RestoreAuthTokenResponse) {} 622 }