blob: 867afae4c12822b2d65691ae673ab704298bf9be [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
package oauth2
import (
"fmt"
"time"
"github.com/apache/pulsar-client-go/oauth2/clock"
"github.com/dgrijalva/jwt-go"
"golang.org/x/oauth2"
)
const (
ClaimNameUserName = "https://pulsar.apache.org/username"
)
// Flow abstracts an OAuth 2.0 authentication and authorization flow
type Flow interface {
// Authorize obtains an authorization grant based on an OAuth 2.0 authorization flow.
// The method returns a grant which may contain an initial access token.
Authorize(audience string) (*AuthorizationGrant, error)
}
// AuthorizationGrantRefresher refreshes OAuth 2.0 authorization grant
type AuthorizationGrantRefresher interface {
// Refresh refreshes an authorization grant to contain a fresh access token
Refresh(grant *AuthorizationGrant) (*AuthorizationGrant, error)
}
type AuthorizationGrantType string
const (
// GrantTypeClientCredentials represents a client credentials grant
GrantTypeClientCredentials AuthorizationGrantType = "client_credentials"
// GrantTypeDeviceCode represents a device code grant
GrantTypeDeviceCode AuthorizationGrantType = "device_code"
)
// AuthorizationGrant is a credential representing the resource owner's authorization
// to access its protected resources, and is used by the client to obtain an access token
type AuthorizationGrant struct {
// Type describes the type of authorization grant represented by this structure
Type AuthorizationGrantType `json:"type"`
// Audience is the intended audience of the access tokens
Audience string `json:"audience,omitempty"`
// ClientID is an OAuth2 client identifier used by some flows
ClientID string `json:"client_id,omitempty"`
// ClientCredentials is credentials data for the client credentials grant type
ClientCredentials *KeyFile `json:"client_credentials,omitempty"`
// the token endpoint
TokenEndpoint string `json:"token_endpoint"`
// Token contains an access token in the client credentials grant type,
// and a refresh token in the device authorization grant type
Token *oauth2.Token `json:"token,omitempty"`
}
// TokenResult holds token information
type TokenResult struct {
AccessToken string `json:"access_token"`
IDToken string `json:"id_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
}
// Issuer holds information about the issuer of tokens
type Issuer struct {
IssuerEndpoint string
ClientID string
Audience string
}
func convertToOAuth2Token(token *TokenResult, clock clock.Clock) oauth2.Token {
return oauth2.Token{
AccessToken: token.AccessToken,
TokenType: "bearer",
RefreshToken: token.RefreshToken,
Expiry: clock.Now().Add(time.Duration(token.ExpiresIn) * time.Second),
}
}
// ExtractUserName extracts the username claim from an authorization grant
func ExtractUserName(token oauth2.Token) (string, error) {
p := jwt.Parser{}
claims := jwt.MapClaims{}
if _, _, err := p.ParseUnverified(token.AccessToken, claims); err != nil {
return "", fmt.Errorf("unable to decode the access token: %v", err)
}
username, ok := claims[ClaimNameUserName]
if !ok {
return "", fmt.Errorf("access token doesn't contain a username claim")
}
switch v := username.(type) {
case string:
return v, nil
default:
return "", fmt.Errorf("access token contains an unsupported username claim")
}
}