diff --git a/go.mod b/go.mod index 8295461..b4c6528 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Dnlbb/chat-server go 1.22.5 require ( - github.com/Dnlbb/auth v0.0.0-20241015204829-7b1556231b70 + github.com/Dnlbb/auth v0.0.0-20241123185353-e5461bcac8db github.com/Dnlbb/platform_common v0.0.0-20241104122631-d7f17ae48ff2 github.com/Masterminds/squirrel v1.5.4 github.com/brianvoe/gofakeit/v6 v6.28.0 @@ -14,7 +14,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/rs/cors v1.11.1 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 diff --git a/go.sum b/go.sum index d39da33..666253d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Dnlbb/auth v0.0.0-20241015204829-7b1556231b70 h1:/OfiXdqCE2hWuplwdfongxuJ4yWA/oqyyw5IMWsfy8Q= -github.com/Dnlbb/auth v0.0.0-20241015204829-7b1556231b70/go.mod h1:s4KkLeUqmD90srrP8dDU3bbWSlZmuZj1MmFCU7eR2/o= +github.com/Dnlbb/auth v0.0.0-20241123185353-e5461bcac8db h1:UpXT8CHtuoKSTct1w3mh/N/OArymnEJj3OszdSV1I74= +github.com/Dnlbb/auth v0.0.0-20241123185353-e5461bcac8db/go.mod h1:eJ6Hya97vTR56gutD+E18OceG2FoevleAaotKbdUpbE= github.com/Dnlbb/platform_common v0.0.0-20241104122631-d7f17ae48ff2 h1:hhCF5Hb1/FjMzKLLGvrquZJZqnJl+XSoMLoD5WpABrY= github.com/Dnlbb/platform_common v0.0.0-20241104122631-d7f17ae48ff2/go.mod h1:1rRSHI08ymQM2D6FgIGfyX7fU/RT5bA8+CYrkBLAYbM= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= @@ -54,13 +54,13 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= diff --git a/internal/app/app.go b/internal/app/app.go index 9b1c11f..83bdab4 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -74,7 +74,9 @@ func (a *App) initServiceProvider(_ context.Context) error { } func (a *App) initGRPCServer(ctx context.Context) error { - a.grpcServer = grpc.NewServer(grpc.Creds(insecure.NewCredentials()), grpc.UnaryInterceptor(interceptor.ValidateInterceptor)) + interceptors := grpc.ChainUnaryInterceptor(interceptor.ValidateInterceptor, a.serviceProvider.GetAuthInterceptor(ctx).AccessInterceptor) + + a.grpcServer = grpc.NewServer(grpc.Creds(insecure.NewCredentials()), interceptors) reflection.Register(a.grpcServer) diff --git a/internal/app/service_provider.go b/internal/app/service_provider.go index d55094a..f97c716 100644 --- a/internal/app/service_provider.go +++ b/internal/app/service_provider.go @@ -4,12 +4,16 @@ import ( "context" "log" - authv1 "github.com/Dnlbb/auth/pkg/auth_v1" + "github.com/Dnlbb/auth/pkg/auth_v1" + userv1 "github.com/Dnlbb/auth/pkg/user_v1" "github.com/Dnlbb/chat-server/internal/api/chat" "github.com/Dnlbb/chat-server/internal/config" + accessInterceptor "github.com/Dnlbb/chat-server/internal/interceptor/access" + accessRepo "github.com/Dnlbb/chat-server/internal/repository/access" "github.com/Dnlbb/chat-server/internal/repository/authrepo" "github.com/Dnlbb/chat-server/internal/repository/postgres/storage" "github.com/Dnlbb/chat-server/internal/repository/repointerface" + "github.com/Dnlbb/chat-server/internal/service/access" "github.com/Dnlbb/chat-server/internal/service/chatserv" "github.com/Dnlbb/chat-server/internal/service/servinterfaces" "github.com/Dnlbb/platform_common/pkg/closer" @@ -25,15 +29,20 @@ type serviceProvider struct { httpConfig config.HTTPConfig swaggerConfig config.SwaggerConf - dbClient db.Client - txManager db.TxManager - chatRepository repointerface.StorageInterface - authRepository repointerface.AuthInterface + dbClient db.Client + txManager db.TxManager - chatService servinterfaces.ChatService - authClient authv1.AuthClient + chatRepository repointerface.StorageInterface + authRepository repointerface.AuthInterface + accessRepository repointerface.Access + userClient userv1.UserApiClient + authClient auth_v1.AuthClient - authController *chat.Controller + chatService servinterfaces.ChatService + accessService servinterfaces.Access + + authController *chat.Controller + authInterceptor *accessInterceptor.AuthInterceptor } func newServiceProvider() *serviceProvider { @@ -134,12 +143,20 @@ func (s *serviceProvider) GetChatRepository(ctx context.Context) repointerface.S func (s *serviceProvider) GetAuthRepository(_ context.Context) repointerface.AuthInterface { if s.authRepository == nil { - s.authRepository = authrepo.NewAuthRepo(s.GetAuthClient()) + s.authRepository = authrepo.NewAuthRepo(s.GetUserClient()) } return s.authRepository } +func (s *serviceProvider) GetAccessRepository(_ context.Context) repointerface.Access { + if s.accessRepository == nil { + s.accessRepository = accessRepo.NewAccessRepo(s.GetAuthClient()) + } + + return s.accessRepository +} + // GetAuthService инициализация сервиса авторизации. func (s *serviceProvider) GetAuthService(ctx context.Context) servinterfaces.ChatService { if s.chatService == nil { @@ -152,7 +169,29 @@ func (s *serviceProvider) GetAuthService(ctx context.Context) servinterfaces.Cha return s.chatService } -func (s *serviceProvider) GetAuthClient() authv1.AuthClient { +func (s *serviceProvider) GetAccessService(ctx context.Context) servinterfaces.Access { + if s.accessService == nil { + s.accessService = access.NewAccessService(s.GetAccessRepository(ctx)) + } + + return s.accessService +} + +func (s *serviceProvider) GetUserClient() userv1.UserApiClient { + if s.userClient == nil { + conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + closer.Add(conn.Close) + + s.userClient = userv1.NewUserApiClient(conn) + } + + return s.userClient +} + +func (s *serviceProvider) GetAuthClient() auth_v1.AuthClient { if s.authClient == nil { conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure()) if err != nil { @@ -160,7 +199,7 @@ func (s *serviceProvider) GetAuthClient() authv1.AuthClient { } closer.Add(conn.Close) - s.authClient = authv1.NewAuthClient(conn) + s.authClient = auth_v1.NewAuthClient(conn) } return s.authClient @@ -169,9 +208,16 @@ func (s *serviceProvider) GetAuthClient() authv1.AuthClient { // GetChatController инициализация контроллера. func (s *serviceProvider) GetChatController(ctx context.Context) *chat.Controller { if s.authController == nil { - s.authController = chat.NewController(s.GetAuthService(ctx)) } return s.authController } + +func (s *serviceProvider) GetAuthInterceptor(ctx context.Context) accessInterceptor.AuthInterceptor { + if s.authInterceptor == nil { + s.authInterceptor = accessInterceptor.NewAuthInterceptor(s.GetAccessService(ctx)) + } + + return *s.authInterceptor +} diff --git a/internal/interceptor/access/access.go b/internal/interceptor/access/access.go new file mode 100644 index 0000000..0ab21f0 --- /dev/null +++ b/internal/interceptor/access/access.go @@ -0,0 +1,16 @@ +package access + +import ( + "context" + + "google.golang.org/grpc" +) + +// AccessInterceptor интерцептор для авторизации. +func (a AuthInterceptor) AccessInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + if err := a.AccessService.Access(ctx, info.FullMethod); err != nil { + return nil, err + } + + return handler(ctx, req) +} diff --git a/internal/interceptor/access/controller.go b/internal/interceptor/access/controller.go new file mode 100644 index 0000000..2a31899 --- /dev/null +++ b/internal/interceptor/access/controller.go @@ -0,0 +1,17 @@ +package access + +import ( + "github.com/Dnlbb/chat-server/internal/service/servinterfaces" +) + +// AuthInterceptor структура реализующая интерцептор для авторизации. +type AuthInterceptor struct { + AccessService servinterfaces.Access +} + +// NewAuthInterceptor конструктор для структуры реализующей интерцептор для авторизации. +func NewAuthInterceptor(accessService servinterfaces.Access) *AuthInterceptor { + return &AuthInterceptor{ + AccessService: accessService, + } +} diff --git a/internal/repository/access/access.go b/internal/repository/access/access.go new file mode 100644 index 0000000..19c0ae9 --- /dev/null +++ b/internal/repository/access/access.go @@ -0,0 +1,13 @@ +package access + +import ( + "context" + + "github.com/Dnlbb/auth/pkg/auth_v1" +) + +// Access вызываем сервис авторизации для проверки доступа. +func (repo RepoAccess) Access(ctx context.Context, path string) error { + _, err := repo.client.Check(ctx, &auth_v1.CheckRequest{EndpointAddress: path}) + return err +} diff --git a/internal/repository/access/accessrepo.go b/internal/repository/access/accessrepo.go new file mode 100644 index 0000000..4492e47 --- /dev/null +++ b/internal/repository/access/accessrepo.go @@ -0,0 +1,15 @@ +package access + +import ( + "github.com/Dnlbb/auth/pkg/auth_v1" +) + +// RepoAccess структура реализующая доступ к клиенту для интерцептора. +type RepoAccess struct { + client auth_v1.AuthClient +} + +// NewAccessRepo конструктор для структуры реализующей доступ к клиенту для интерцептора. +func NewAccessRepo(client auth_v1.AuthClient) *RepoAccess { + return &RepoAccess{client: client} +} diff --git a/internal/repository/authrepo/authrepo.go b/internal/repository/authrepo/authrepo.go index 8d0cb24..3d03f3d 100644 --- a/internal/repository/authrepo/authrepo.go +++ b/internal/repository/authrepo/authrepo.go @@ -1,16 +1,16 @@ package authrepo import ( - "github.com/Dnlbb/auth/pkg/auth_v1" + "github.com/Dnlbb/auth/pkg/user_v1" "github.com/Dnlbb/chat-server/internal/repository/repointerface" ) // AuthRepo структура реализующая методы для похода в сервис auth за айдишниками пользователей. type AuthRepo struct { - authClient auth_v1.AuthClient + authClient user_v1.UserApiClient } // NewAuthRepo конструктор для AuthRepo. -func NewAuthRepo(authClient auth_v1.AuthClient) repointerface.AuthInterface { +func NewAuthRepo(authClient user_v1.UserApiClient) repointerface.AuthInterface { return AuthRepo{authClient: authClient} } diff --git a/internal/repository/authrepo/get_ids.go b/internal/repository/authrepo/get_ids.go index f6bd7e7..66704c2 100644 --- a/internal/repository/authrepo/get_ids.go +++ b/internal/repository/authrepo/get_ids.go @@ -5,7 +5,7 @@ import ( "context" "fmt" - authv1 "github.com/Dnlbb/auth/pkg/auth_v1" + authv1 "github.com/Dnlbb/auth/pkg/user_v1" "github.com/Dnlbb/chat-server/internal/models" ) diff --git a/internal/repository/repointerface/interfaces.go b/internal/repository/repointerface/interfaces.go index deee2fe..19e5c87 100644 --- a/internal/repository/repointerface/interfaces.go +++ b/internal/repository/repointerface/interfaces.go @@ -18,3 +18,8 @@ type StorageInterface interface { type AuthInterface interface { GetIDs(ctx context.Context, usernames models.Usernames) ([]models.ID, error) } + +// Access интерфейс для проверки доступа. +type Access interface { + Access(ctx context.Context, path string) error +} diff --git a/internal/service/access/access.go b/internal/service/access/access.go new file mode 100644 index 0000000..f215280 --- /dev/null +++ b/internal/service/access/access.go @@ -0,0 +1,36 @@ +package access + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/grpc/metadata" +) + +const ( + authPrefix = "Bearer " +) + +// Access метод дял проверки доступа пользователя. +func (as ServiceAccess) Access(ctx context.Context, path string) error { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return fmt.Errorf("metadata error") + } + + authHeader, ok := md["authorization"] + if !ok || len(authHeader) == 0 { + return fmt.Errorf("authorization header is not provided") + } + + if !strings.HasPrefix(authHeader[0], authPrefix) { + return fmt.Errorf("invalid authorization header format") + } + + accessToken := strings.TrimPrefix(authHeader[0], authPrefix) + mod := metadata.New(map[string]string{"Authorization": "Bearer " + accessToken}) + clientCtx := metadata.NewOutgoingContext(ctx, mod) + + return as.accessRepository.Access(clientCtx, path) +} diff --git a/internal/service/access/service.go b/internal/service/access/service.go new file mode 100644 index 0000000..d4ae769 --- /dev/null +++ b/internal/service/access/service.go @@ -0,0 +1,18 @@ +package access + +import ( + "github.com/Dnlbb/chat-server/internal/repository/repointerface" + "github.com/Dnlbb/chat-server/internal/service/servinterfaces" +) + +// ServiceAccess структура реализующая сервис интерцептора. +type ServiceAccess struct { + accessRepository repointerface.Access +} + +// NewAccessService конструктор для сервиса интерцептора. +func NewAccessService(accessRepository repointerface.Access) servinterfaces.Access { + return &ServiceAccess{ + accessRepository: accessRepository, + } +} diff --git a/internal/service/servinterfaces/interfaces.go b/internal/service/servinterfaces/interfaces.go index a98ee01..d007d17 100644 --- a/internal/service/servinterfaces/interfaces.go +++ b/internal/service/servinterfaces/interfaces.go @@ -12,3 +12,8 @@ type ChatService interface { Delete(ctx context.Context, chat models.Chat) error SendMessage(ctx context.Context, message models.Message) error } + +// Access интерфейс реализующий проверку. +type Access interface { + Access(ctx context.Context, path string) error +} diff --git a/vendor.protogen/google/api/annotations.proto b/vendor.protogen/google/api/annotations.proto new file mode 100644 index 0000000..84c4816 --- /dev/null +++ b/vendor.protogen/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/vendor.protogen/google/api/auth.proto b/vendor.protogen/google/api/auth.proto new file mode 100644 index 0000000..19d5924 --- /dev/null +++ b/vendor.protogen/google/api/auth.proto @@ -0,0 +1,237 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "AuthProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Authentication` defines the authentication configuration for API methods +// provided by an API service. +// +// Example: +// +// name: calendar.googleapis.com +// authentication: +// providers: +// - id: google_calendar_auth +// jwks_uri: https://www.googleapis.com/oauth2/v1/certs +// issuer: https://securetoken.google.com +// rules: +// - selector: "*" +// requirements: +// provider_id: google_calendar_auth +// - selector: google.calendar.Delegate +// oauth: +// canonical_scopes: https://www.googleapis.com/auth/calendar.read +message Authentication { + // A list of authentication rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated AuthenticationRule rules = 3; + + // Defines a set of authentication providers that a service supports. + repeated AuthProvider providers = 4; +} + +// Authentication rules for the service. +// +// By default, if a method has any authentication requirements, every request +// must include a valid credential matching one of the requirements. +// It's an error to include more than one kind of credential in a single +// request. +// +// If a method doesn't have any auth requirements, request credentials will be +// ignored. +message AuthenticationRule { + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // The requirements for OAuth credentials. + OAuthRequirements oauth = 2; + + // If true, the service accepts API keys without any other credential. + // This flag only applies to HTTP and gRPC requests. + bool allow_without_credential = 5; + + // Requirements for additional authentication providers. + repeated AuthRequirement requirements = 7; +} + +// Specifies a location to extract JWT from an API request. +message JwtLocation { + oneof in { + // Specifies HTTP header name to extract JWT token. + string header = 1; + + // Specifies URL query parameter name to extract JWT token. + string query = 2; + + // Specifies cookie name to extract JWT token. + string cookie = 4; + } + + // The value prefix. The value format is "value_prefix{token}" + // Only applies to "in" header type. Must be empty for "in" query type. + // If not empty, the header value has to match (case sensitive) this prefix. + // If not matched, JWT will not be extracted. If matched, JWT will be + // extracted after the prefix is removed. + // + // For example, for "Authorization: Bearer {JWT}", + // value_prefix="Bearer " with a space at the end. + string value_prefix = 3; +} + +// Configuration for an authentication provider, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthProvider { + // The unique identifier of the auth provider. It will be referred to by + // `AuthRequirement.provider_id`. + // + // Example: "bookstore_auth". + string id = 1; + + // Identifies the principal that issued the JWT. See + // https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.1 + // Usually a URL or an email address. + // + // Example: https://securetoken.google.com + // Example: 1234567-compute@developer.gserviceaccount.com + string issuer = 2; + + // URL of the provider's public key set to validate signature of the JWT. See + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + // Optional if the key set document: + // - can be retrieved from + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) + // of the issuer. + // - can be inferred from the email domain of the issuer (e.g. a Google + // service account). + // + // Example: https://www.googleapis.com/oauth2/v1/certs + string jwks_uri = 3; + + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, JWTs with audiences: + // - "https://[service.name]/[google.protobuf.Api.name]" + // - "https://[service.name]/" + // will be accepted. + // For example, if no audiences are in the setting, LibraryService API will + // accept JWTs with the following audiences: + // - + // https://library-example.googleapis.com/google.example.library.v1.LibraryService + // - https://library-example.googleapis.com/ + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 4; + + // Redirect URL if JWT token is required but not present or is expired. + // Implement authorizationUrl of securityDefinitions in OpenAPI spec. + string authorization_url = 5; + + // Defines the locations to extract the JWT. For now it is only used by the + // Cloud Endpoints to store the OpenAPI extension [x-google-jwt-locations] + // (https://cloud.google.com/endpoints/docs/openapi/openapi-extensions#x-google-jwt-locations) + // + // JWT locations can be one of HTTP headers, URL query parameters or + // cookies. The rule is that the first match wins. + // + // If not specified, default to use following 3 locations: + // 1) Authorization: Bearer + // 2) x-goog-iap-jwt-assertion + // 3) access_token query parameter + // + // Default locations can be specified as followings: + // jwt_locations: + // - header: Authorization + // value_prefix: "Bearer " + // - header: x-goog-iap-jwt-assertion + // - query: access_token + repeated JwtLocation jwt_locations = 6; +} + +// OAuth scopes are a way to define data and permissions on data. For example, +// there are scopes defined for "Read-only access to Google Calendar" and +// "Access to Cloud Platform". Users can consent to a scope for an application, +// giving it permission to access that data on their behalf. +// +// OAuth scope specifications should be fairly coarse grained; a user will need +// to see and understand the text description of what your scope means. +// +// In most cases: use one or at most two OAuth scopes for an entire family of +// products. If your product has multiple APIs, you should probably be sharing +// the OAuth scope across all of those APIs. +// +// When you need finer grained OAuth consent screens: talk with your product +// management about how developers will use them in practice. +// +// Please note that even though each of the canonical scopes is enough for a +// request to be accepted and passed to the backend, a request can still fail +// due to the backend requiring additional scopes or permissions. +message OAuthRequirements { + // The list of publicly documented OAuth scopes that are allowed access. An + // OAuth token containing any of these scopes will be accepted. + // + // Example: + // + // canonical_scopes: https://www.googleapis.com/auth/calendar, + // https://www.googleapis.com/auth/calendar.read + string canonical_scopes = 1; +} + +// User-defined authentication requirements, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthRequirement { + // [id][google.api.AuthProvider.id] from authentication provider. + // + // Example: + // + // provider_id: bookstore_auth + string provider_id = 1; + + // NOTE: This will be deprecated soon, once AuthProvider.audiences is + // implemented and accepted in all the runtime components. + // + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, only JWTs with audience + // "https://[Service_name][google.api.Service.name]/[API_name][google.protobuf.Api.name]" + // will be accepted. For example, if no audiences are in the setting, + // LibraryService API will only accept JWTs with the following audience + // "https://library-example.googleapis.com/google.example.library.v1.LibraryService". + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 2; +}