mirror of
https://github.com/davidallendj/opaal.git
synced 2025-12-20 11:37:01 -07:00
Refactored and added client credentials flow
This commit is contained in:
parent
f912890a2d
commit
f490eb4fc4
9 changed files with 113 additions and 41 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
opaal "davidallendj/opaal/internal"
|
"davidallendj/opaal/internal/flows"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ var loginCmd = &cobra.Command{
|
||||||
Short: "Start the login flow",
|
Short: "Start the login flow",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
for {
|
for {
|
||||||
err := opaal.Login(&config)
|
err := flows.Login(&config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%v\n", err)
|
fmt.Printf("%v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
@ -37,5 +37,6 @@ func init() {
|
||||||
loginCmd.Flags().BoolVar(&config.DecodeIdToken, "decode-id-token", config.DecodeIdToken, "decode and print ID token from identity provider")
|
loginCmd.Flags().BoolVar(&config.DecodeIdToken, "decode-id-token", config.DecodeIdToken, "decode and print ID token from identity provider")
|
||||||
loginCmd.Flags().BoolVar(&config.DecodeAccessToken, "decore-access-token", config.DecodeAccessToken, "decode and print access token from authorization server")
|
loginCmd.Flags().BoolVar(&config.DecodeAccessToken, "decore-access-token", config.DecodeAccessToken, "decode and print access token from authorization server")
|
||||||
loginCmd.Flags().BoolVar(&config.RunOnce, "once", config.RunOnce, "set whether to run login once and exit")
|
loginCmd.Flags().BoolVar(&config.RunOnce, "once", config.RunOnce, "set whether to run login once and exit")
|
||||||
|
loginCmd.Flags().StringVar(&config.GrantType, "grant-type", config.GrantType, "set the grant-type/authorization flow")
|
||||||
rootCmd.AddCommand(loginCmd)
|
rootCmd.AddCommand(loginCmd)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
cmd/root.go
24
cmd/root.go
|
|
@ -21,6 +21,18 @@ var rootCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to start CLI: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cobra.OnInitialize(initConfig)
|
||||||
|
rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "set the config path")
|
||||||
|
}
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
// load config if found or create a new one
|
// load config if found or create a new one
|
||||||
if configPath != "" {
|
if configPath != "" {
|
||||||
|
|
@ -35,15 +47,3 @@ func initConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to start CLI: %s", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cobra.OnInitialize(initConfig)
|
|
||||||
rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "set the config path")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ urls:
|
||||||
jwks_uri: http://git.towk.local:3000/login/oauth/keys
|
jwks_uri: http://git.towk.local:3000/login/oauth/keys
|
||||||
login: http://127.0.0.1:4433/self-service/login/api
|
login: http://127.0.0.1:4433/self-service/login/api
|
||||||
login-flow-id: http://127.0.0.1:4433/self-service/login/flows?id={id}
|
login-flow-id: http://127.0.0.1:4433/self-service/login/flows?id={id}
|
||||||
|
register-client: http://127.0.0.1:4445/clients
|
||||||
|
authorize-client: http://127.0.0.1:4444/oauth2/authorize
|
||||||
state: ""
|
state: ""
|
||||||
response-type: code
|
response-type: code
|
||||||
decode-id-token: true
|
decode-id-token: true
|
||||||
|
|
|
||||||
|
|
@ -129,12 +129,17 @@ func (client *Client) FetchCSRFToken(flowUrl string) error {
|
||||||
func (client *Client) FetchTokenFromAuthenticationServer(code string, remoteUrl string, state string) ([]byte, error) {
|
func (client *Client) FetchTokenFromAuthenticationServer(code string, remoteUrl string, state string) ([]byte, error) {
|
||||||
data := url.Values{
|
data := url.Values{
|
||||||
"grant_type": {"authorization_code"},
|
"grant_type": {"authorization_code"},
|
||||||
"code": {code},
|
|
||||||
"client_id": {client.Id},
|
"client_id": {client.Id},
|
||||||
"client_secret": {client.Secret},
|
"client_secret": {client.Secret},
|
||||||
"state": {state},
|
|
||||||
"redirect_uri": {strings.Join(client.RedirectUris, ",")},
|
"redirect_uri": {strings.Join(client.RedirectUris, ",")},
|
||||||
}
|
}
|
||||||
|
// add optional params if valid
|
||||||
|
if code != "" {
|
||||||
|
data["code"] = []string{code}
|
||||||
|
}
|
||||||
|
if state != "" {
|
||||||
|
data["state"] = []string{state}
|
||||||
|
}
|
||||||
res, err := http.PostForm(remoteUrl, data)
|
res, err := http.PostForm(remoteUrl, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get ID token: %s", err)
|
return nil, fmt.Errorf("failed to get ID token: %s", err)
|
||||||
|
|
@ -151,9 +156,16 @@ func (client *Client) FetchTokenFromAuthorizationServer(remoteUrl string, jwt st
|
||||||
// hydra endpoint: /oauth/token
|
// hydra endpoint: /oauth/token
|
||||||
data := "grant_type=" + url.QueryEscape("urn:ietf:params:oauth:grant-type:jwt-bearer") +
|
data := "grant_type=" + url.QueryEscape("urn:ietf:params:oauth:grant-type:jwt-bearer") +
|
||||||
"&client_id=" + client.Id +
|
"&client_id=" + client.Id +
|
||||||
"&client_secret=" + client.Secret +
|
"&client_secret=" + client.Secret
|
||||||
"&scope=" + strings.Join(scope, "+") +
|
|
||||||
"&assertion=" + jwt
|
// add optional params if valid
|
||||||
|
if jwt != "" {
|
||||||
|
data += "&assertion=" + jwt
|
||||||
|
}
|
||||||
|
if scope != nil || len(scope) > 0 {
|
||||||
|
data += "&scope=" + strings.Join(scope, "+")
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("encoded params: %v\n\n", data)
|
fmt.Printf("encoded params: %v\n\n", data)
|
||||||
req, err := http.NewRequest("POST", remoteUrl, bytes.NewBuffer([]byte(data)))
|
req, err := http.NewRequest("POST", remoteUrl, bytes.NewBuffer([]byte(data)))
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,15 @@ func NewConfig() Config {
|
||||||
ResponseType: "code",
|
ResponseType: "code",
|
||||||
Scope: []string{"openid", "profile", "email"},
|
Scope: []string{"openid", "profile", "email"},
|
||||||
ActionUrls: ActionUrls{
|
ActionUrls: ActionUrls{
|
||||||
Identities: "",
|
Identities: "",
|
||||||
AccessToken: "",
|
AccessToken: "",
|
||||||
TrustedIssuers: "",
|
TrustedIssuers: "",
|
||||||
ServerConfig: "",
|
ServerConfig: "",
|
||||||
|
JwksUri: "",
|
||||||
|
Login: "",
|
||||||
|
LoginFlowId: "",
|
||||||
|
RegisterClient: "",
|
||||||
|
AuthorizeClient: "",
|
||||||
},
|
},
|
||||||
OpenBrowser: false,
|
OpenBrowser: false,
|
||||||
DecodeIdToken: false,
|
DecodeIdToken: false,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package opaal
|
package flows
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
opaal "davidallendj/opaal/internal"
|
||||||
"davidallendj/opaal/internal/oidc"
|
"davidallendj/opaal/internal/oidc"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -12,15 +13,7 @@ import (
|
||||||
"github.com/davidallendj/go-utils/util"
|
"github.com/davidallendj/go-utils/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(config *Config) error {
|
func AuthorizationCode(config *opaal.Config, server *opaal.Server, client *opaal.Client) error {
|
||||||
if config == nil {
|
|
||||||
return fmt.Errorf("config is not valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize client that will be used throughout login flow
|
|
||||||
server := NewServerWithConfig(config)
|
|
||||||
client := NewClientWithConfig(config)
|
|
||||||
|
|
||||||
// initiate the login flow and get a flow ID and CSRF token
|
// initiate the login flow and get a flow ID and CSRF token
|
||||||
{
|
{
|
||||||
err := client.InitiateLoginFlow(config.ActionUrls.Login)
|
err := client.InitiateLoginFlow(config.ActionUrls.Login)
|
||||||
|
|
@ -49,7 +42,7 @@ func Login(config *Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if all appropriate parameters are set in config
|
// check if all appropriate parameters are set in config
|
||||||
if !HasRequiredParams(config) {
|
if !opaal.HasRequiredParams(config) {
|
||||||
return fmt.Errorf("client ID must be set")
|
return fmt.Errorf("client ID must be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
29
internal/flows/client_credentials.go
Normal file
29
internal/flows/client_credentials.go
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
package flows
|
||||||
|
|
||||||
|
import (
|
||||||
|
opaal "davidallendj/opaal/internal"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ClientCredentials(config *opaal.Config, server *opaal.Server, client *opaal.Client) error {
|
||||||
|
// register a new OAuth 2 client with authorization srever
|
||||||
|
_, err := client.RegisterOAuthClient(config.ActionUrls.RegisterClient, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to register OAuth client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// authorize the client
|
||||||
|
_, err = client.AuthorizeClient(config.ActionUrls.AuthorizeClient)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to authorize client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// request a token from the authorization server
|
||||||
|
res, err := client.FetchTokenFromAuthorizationServer(config.ActionUrls.AccessToken, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch token from authorization server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("token: %v\n", string(res))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
28
internal/flows/login.go
Normal file
28
internal/flows/login.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package flows
|
||||||
|
|
||||||
|
import (
|
||||||
|
opaal "davidallendj/opaal/internal"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Login(config *opaal.Config) error {
|
||||||
|
if config == nil {
|
||||||
|
return fmt.Errorf("config is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize client that will be used throughout login flow
|
||||||
|
server := opaal.NewServerWithConfig(config)
|
||||||
|
client := opaal.NewClientWithConfig(config)
|
||||||
|
|
||||||
|
fmt.Printf("grant type: %v\n", config.GrantType)
|
||||||
|
|
||||||
|
if config.GrantType == "authorization_code" {
|
||||||
|
AuthorizationCode(config, server, client)
|
||||||
|
} else if config.GrantType == "client_credentials" {
|
||||||
|
ClientCredentials(config, server, client)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid grant type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
package opaal
|
package opaal
|
||||||
|
|
||||||
type ActionUrls struct {
|
type ActionUrls struct {
|
||||||
Identities string `yaml:"identities"`
|
Identities string `yaml:"identities"`
|
||||||
TrustedIssuers string `yaml:"trusted-issuers"`
|
TrustedIssuers string `yaml:"trusted-issuers"`
|
||||||
AccessToken string `yaml:"access-token"`
|
AccessToken string `yaml:"access-token"`
|
||||||
ServerConfig string `yaml:"server-config"`
|
ServerConfig string `yaml:"server-config"`
|
||||||
JwksUri string `yaml:"jwks_uri"`
|
JwksUri string `yaml:"jwks_uri"`
|
||||||
Login string `yaml:"login"`
|
Login string `yaml:"login"`
|
||||||
LoginFlowId string `yaml:"login-flow-id"`
|
LoginFlowId string `yaml:"login-flow-id"`
|
||||||
|
RegisterClient string `yaml:"register-client"`
|
||||||
|
AuthorizeClient string `yaml:"authorize-client"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasRequiredParams(config *Config) bool {
|
func HasRequiredParams(config *Config) bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue