Commit 6e428d07 authored by Simon Schürg's avatar Simon Schürg 🚀
Browse files

Simplify http requests with go-resty and fix token refresh command

parent 8fd469d1
......@@ -180,7 +180,7 @@ var refreshCmd = &cobra.Command{
Short: "Performs OpenID Connect refresh",
Long: `Performs OpenID Connect refresh`,
Run: func(cmd *cobra.Command, args []string) {
tokenSet := internal.RefreshToken(issuer, clientID, clientSecret)
tokenSet := internal.RefreshToken(issuer, clientID, refreshToken)
internal.PrintTokenSet(tokenSet)
if toClipboard {
internal.AccessTokenToClipboard(tokenSet)
......
......@@ -12,8 +12,8 @@ var userinfoCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
rawToken := args[0]
userinfoToken := internal.UserInfo(rawToken)
internal.PrettyPrintDecodedJWT(userinfoToken.Encoded)
userinfoJson := internal.UserInfo(rawToken)
internal.PrettyPrintJSON([]byte(userinfoJson))
},
}
......
package internal
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"github.com/coreos/go-oidc"
"github.com/go-resty/resty/v2"
"github.com/int128/oauth2cli"
"github.com/int128/oauth2cli/oauth2params"
"github.com/pkg/browser"
......@@ -22,64 +19,52 @@ import (
// RefreshToken uses an existing refresh token to retrieve a new TokenSet
// See https://tools.ietf.org/html/rfc6749#section-6
func RefreshToken(issuer, clientID, refreshToken string) *TokenSet {
provider := FetchOidcMetadata(issuer)
payload := url.Values{}
payload.Add("grant_type", "refresh_token")
payload.Add("client_id", clientID)
payload.Add("refresh_token", refreshToken)
req, err := http.NewRequest("POST", provider.TokenEndpoint, bytes.NewBufferString(payload.Encode()))
FatalOnError(err)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
FatalOnError(err)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
FatalOnError(err)
oidcMeta := FetchOidcMetadata(issuer)
client := resty.New().R()
client.SetFormData(map[string]string{
"grant_type": "refresh_token",
"client_id": clientID,
"refresh_token": refreshToken,
})
resp, err := client.Post(oidcMeta.TokenEndpoint)
LogRestyResp(resp, err)
var tokenSet TokenSet
json.Unmarshal(body, &tokenSet)
json.Unmarshal(resp.Body(), &tokenSet)
return &tokenSet
}
// ClientCredenitalsAuth uses a client id and client secret to retrieve a TokenSet
// See https://tools.ietf.org/html/rfc6749#section-4.4
func ClientCredenitalsAuth(issuer, clientID, clientSecret string) *TokenSet {
provider := FetchOidcMetadata(issuer)
payload := url.Values{}
payload.Add("grant_type", "client_credentials")
payload.Add("client_id", clientID)
payload.Add("client_secret", clientSecret)
req, err := http.NewRequest("POST", provider.TokenEndpoint, bytes.NewBufferString(payload.Encode()))
FatalOnError(err)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
FatalOnError(err)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
FatalOnError(err)
oidcMeta := FetchOidcMetadata(issuer)
client := resty.New().R()
client.SetFormData(map[string]string{
"grant_type": "client_credentials",
"client_id": clientID,
"client_secret": clientSecret,
})
resp, err := client.Post(oidcMeta.TokenEndpoint)
LogRestyResp(resp, err)
var tokenSet TokenSet
json.Unmarshal(body, &tokenSet)
json.Unmarshal(resp.Body(), &tokenSet)
return &tokenSet
}
// ResourceOwnerCredentialsAuth uses a username and password to retrieve a TokenSet
// See https://tools.ietf.org/html/rfc6749#section-10.7
func ResourceOwnerCredentialsAuth(issuer, clientID, username, password string) *TokenSet {
provider := FetchOidcMetadata(issuer)
payload := url.Values{}
payload.Add("grant_type", "password")
payload.Add("client_id", clientID)
payload.Add("username", username)
payload.Add("password", password)
req, err1 := http.NewRequest("POST", provider.TokenEndpoint, bytes.NewBufferString(payload.Encode()))
FatalOnError(err1)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
FatalOnError(err)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
FatalOnError(err)
oidcMeta := FetchOidcMetadata(issuer)
client := resty.New().R()
client.SetFormData(map[string]string{
"grant_type": "password",
"client_id": clientID,
"username": username,
"password": password,
})
resp, err := client.Post(oidcMeta.TokenEndpoint)
LogRestyResp(resp, err)
var tokenSet TokenSet
json.Unmarshal(body, &tokenSet)
json.Unmarshal(resp.Body(), &tokenSet)
return &tokenSet
}
......@@ -156,7 +141,7 @@ func AuthorizationCodeAuth(clientID, clientSecret, openidIssuerURL string) *Toke
// for refresh tokens.
// See https://tools.ietf.org/html/rfc6749#section-4.2
func ImplicitAuth(clientID, clientSecret, openidIssuerURL string) *TokenSet {
// log.Fatalln("Not yet implemented")
log.Fatalln("Not yet implemented")
browser.OpenURL("https://auth.schuerg.net/auth/realms/playground/protocol/openid-connect/auth")
return nil
}
......@@ -182,17 +167,12 @@ func TokenRevocation() {
// UserInfo fetches the user info OIDC endpoint and returns the result.
// The result is a userinfo token -- also a JWT.
func UserInfo(accessToken string) *Token {
func UserInfo(accessToken string) string {
token := DecodeToken([]byte(accessToken))
oidcMeta := FetchOidcMetadata(token.GetRegisteredClaims().Issuer)
req, err := http.NewRequest("GET", oidcMeta.UserinfoEndpoint, nil)
FatalOnError(err)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
client := &http.Client{}
resp, err := client.Do(req)
FatalOnError(err)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
FatalOnError(err)
return DecodeToken(body)
client := resty.New()
client.SetAuthToken(accessToken)
resp, err := client.R().Get(oidcMeta.UserinfoEndpoint)
LogRestyResp(resp, err)
return string(resp.Body())
}
......@@ -7,11 +7,8 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"io/ioutil"
"log"
"math/big"
"net/http"
"net/url"
"strings"
"github.com/go-resty/resty/v2"
......@@ -41,47 +38,41 @@ type TokenSet struct {
// OpenIDProviderMetadata is the description of the OpenID Providers configuration.
// This information can be fetched from a well known URL.
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
type OpenIDProviderMetadata struct {
Issuer string `json:"issuer" storm:"id"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
JwksURI string `json:"jwks_uri"`
RegistrationEndpoint string `json:"registration_endpoint"`
ScopesSupported []string `json:"scopes_supported"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported"`
GrantTypesSupported []string `json:"grant_types_supported"`
AcrValuesSupported []string `json:"acr_values_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported"`
IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported"`
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported"`
UserinfoEncryptionAlgValuesSupported []string `json:"userinfo_encryption_alg_values_supported"`
UserinfoEncryptionEncValuesSupported []string `json:"userinfo_encryption_enc_values_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported"`
RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"`
DisplayValuesSupported []string `json:"display_values_supported"`
ClaimTypesSupported []string `json:"claim_types_supported"`
ClaimsSupported []string `json:"claims_supported"`
ServiceDocumentation string `json:"service_documentation"`
ClaimsLocalesSupported bool `json:"claims_locales_supported"`
UILocalesSupported []string `json:"ui_locales_supported"`
ClaimsParameterSupported bool `json:"claims_parameter_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
RequestURIRegistration bool `json:"require_request_uri_registration"`
OpPolicyURI []string `json:"op_policy_uri"`
OpTosURI []string `json:"op_tos_uri"`
IntrospectionEndpoint string `json:"introspection_endpoint"`
TLSClientCertificateBoundAccessTokens bool `json:"tls_client_certificate_bound_access_tokens"`
EndSessionEndpoint string `json:"end_session_endpoint"`
CheckSessionIframe string `json:"check_session_iframe"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
type OIDCMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
IntrospectionEndpoint string `json:"introspection_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
EndSessionEndpoint string `json:"end_session_endpoint"`
JwksURI string `json:"jwks_uri"`
CheckSessionIframe string `json:"check_session_iframe"`
GrantTypesSupported []string `json:"grant_types_supported"`
ResponseTypesSupported []string `json:"response_types_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported"`
IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported"`
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
ResponseModesSupported []string `json:"response_modes_supported"`
RegistrationEndpoint string `json:"registration_endpoint"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"`
ClaimsSupported []string `json:"claims_supported"`
ClaimTypesSupported []string `json:"claim_types_supported"`
ClaimsParameterSupported bool `json:"claims_parameter_supported"`
ScopesSupported []string `json:"scopes_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
RequireRequestURIRegistration bool `json:"require_request_uri_registration"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
TLSClientCertificateBoundAccessTokens bool `json:"tls_client_certificate_bound_access_tokens"`
RevocationEndpoint string `json:"revocation_endpoint"`
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported"`
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported"`
BackchannelLogoutSupported bool `json:"backchannel_logout_supported"`
BackchannelLogoutSessionSupported bool `json:"backchannel_logout_session_supported"`
}
type JWKS struct {
......@@ -138,7 +129,7 @@ type JWS struct{}
// Token is an OAuth Token
type Token struct {
Encoded string `storm:"id"`
Encoded string
Header map[string]interface{}
Payload map[string]interface{}
}
......@@ -201,7 +192,7 @@ type JWTRegisteredClaims struct {
Subject string `json:"sub"`
Audience string `json:"aud"`
ExpirationTime int `json:"exp"`
NotBefore string `json:"nbf"`
NotBefore int `json:"nbf"`
IssuedAt int `json:"iat"`
JWTID string `json:"jit"`
}
......@@ -252,33 +243,26 @@ type JWKSet struct {
// DiscoverOidcMetadata fetches OpenID Connect Provider configuration
// from an issuer URL
func FetchOidcMetadata(issuerURL string) OpenIDProviderMetadata {
func FetchOidcMetadata(issuerURL string) OIDCMetadata {
openidConfigURL := strings.TrimRight(issuerURL, "/") + "/.well-known/openid-configuration"
client := resty.New()
resp, err := client.R().Get(openidConfigURL)
LogRestyResp(resp, err)
var openIDProviderMetadata OpenIDProviderMetadata
var openIDProviderMetadata OIDCMetadata
err = json.Unmarshal(resp.Body(), &openIDProviderMetadata)
FatalOnError(err)
return openIDProviderMetadata
}
// FetchJWKSet fetches all JWKs from a given OpenID Connect Cert URL
func fetchJWKSet(issuer string) *JWKSet {
func FetchJWKSet(issuer string) *JWKSet {
oidcMetadata := FetchOidcMetadata(issuer)
url, err := url.ParseRequestURI(oidcMetadata.JwksURI)
FatalOnError(err)
resp, err := http.Get(url.String())
FatalOnError(err)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
client := resty.New()
resp, err := client.R().Get(oidcMetadata.JwksURI)
FatalOnError(err)
var jwkSet JWKSet
err = json.Unmarshal(body, &jwkSet)
err = json.Unmarshal(resp.Body(), &jwkSet)
FatalOnError(err)
// fmt.Println(jwkSet)
// for _, jwk := range jwkSet.Keys {
// }
return &jwkSet
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment