Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability for BSS to perform client credentials grant for access token #23

Merged
merged 33 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4a14783
Added initial implementation of OAuth registration and token fetch
davidallendj Feb 28, 2024
0265295
Added error checks and access token var
davidallendj Feb 28, 2024
b2d5188
Added AuthorizeClient function to flow
davidallendj Feb 28, 2024
505dc5e
Finalized initial implementation of OAuth client credentials flow
davidallendj Feb 29, 2024
b3a95cf
Add --oauth2-base-url and BSS_OAUTH2_BASE_URL
synackd Feb 29, 2024
de9cc92
Dockerfile: Document BSS_JWKS_URL and BSS_OAUTH2_BASE_URL
synackd Feb 29, 2024
c1ec85f
Merge pull request #1 from synackd/bss-auth-flag-and-env-vars
davidallendj Feb 29, 2024
60e48c7
Move client credentials grant to requestClientCreds()
synackd Feb 29, 2024
b9a225b
OAuth -> OAuth2
synackd Mar 1, 2024
96de4d3
Differentiate admin vs. public OAuth2 base URL
synackd Mar 1, 2024
bb35458
requestClientCreds(): Add log and debug messages
synackd Mar 1, 2024
ce3924a
Add Triad license
synackd Mar 1, 2024
c2e022d
Move RequestClientCreds and accessToken to oauth.go
synackd Mar 2, 2024
4414f04
oauth.go: Add JWT validation/polling functions
synackd Mar 2, 2024
34de2a0
sm.go: Add JWT testing functionality (rm from main.go)
synackd Mar 2, 2024
404c805
Use http.MethodXXX instead of "XXX" string
synackd Mar 2, 2024
8f80f0b
oauth.go: Add JWTTestAndRefresh()
synackd Mar 2, 2024
9484b95
sm.go: Remove JWT refresh code in favor of function
synackd Mar 2, 2024
b7bf17e
getStateFromHSM: Implement JWT authentication
synackd Mar 2, 2024
542ebfa
Try multiple times to test SMD auth enablement
synackd Mar 4, 2024
09a6e1c
RequestClientCreds(): fmt.Printf -> log.Printf
synackd Mar 4, 2024
8225a2e
smClient: Change type from *http.Client to *OAuthClient
synackd Mar 4, 2024
f709047
TestSMAuthEnabled: Add log msg for success
synackd Mar 4, 2024
5665c90
Attach JWT getting functions to OAuthClient, use with smClient
synackd Mar 4, 2024
9df675a
Add smClient nil checks in TestSM* functions
synackd Mar 4, 2024
c27d985
JWTIsValid(): Add debug statements for checking JWT validity date range
synackd Mar 4, 2024
d9b3d75
getStateFromHSM(): Fix wrong error variable (rerr/err)
synackd Mar 4, 2024
5ed2c3d
getStateFromHSM(): Debug responses
synackd Mar 4, 2024
3e22839
Remove printing of access token in logs
synackd Mar 7, 2024
9353c0c
Correct BSS_OAUTH2_USER_BASE_URL IP comment in Dockerfile
synackd Mar 7, 2024
9cd350a
Update copyright year in oauth.go: 2023 -> 2024
synackd Mar 7, 2024
8f40cfa
Merge pull request #2 from synackd/bss-auth-impl-token
davidallendj Mar 8, 2024
b18d281
Fix Errorf/Printf statements with missing args
synackd Mar 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cmd/boot-script-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"log"
Expand Down Expand Up @@ -96,6 +97,7 @@ var (
useSQL = false // Use ETCD by default
authRetryCount = authDefaultRetryCount
jwksURL = ""
accessToken = ""
sqlDbOpts = ""
spireServiceURL = "https://spire-tokens.spire:54440"
)
Expand Down Expand Up @@ -443,6 +445,27 @@ func main() {
}
}

// register oauth client and receive
var client OAuthClient
_, err = client.RegisterOAuthClient("http://127.0.0.1:4444/oauth2/register", []string{})
if err != nil {
log.Fatalf("failed to register OAuth client: %v", err)
}
_, err = client.AuthorizeClient("http://127.0.0.1:4444/oauth2/auth")
if err != nil {
log.Fatalf("failed to authorize OAuth client: %v", err)
}
res, err := client.FetchTokenFromAuthorizationServer("http://127.0.0.1:4444/oauth2/token", []string{})
if err != nil {
log.Fatalf("failed to fetch token from authorization server: %v", err)
}
davidallendj marked this conversation as resolved.
Show resolved Hide resolved

// unmarshal the access token
var resJson map[string]any
json.Unmarshal(res, &resJson)
accessToken = resJson["access_token"].(string)
log.Printf("Access Token: %v\n", accessToken)

var svcOpts string
if insecure {
svcOpts = "insecure,"
Expand Down
101 changes: 101 additions & 0 deletions cmd/boot-script-service/oauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// NOTE: Triad License goes here
package main

import (
"bytes"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)

type OAuthClient struct {
http.Client
Id string
Secret string
RedirectUris []string
}

func (client *OAuthClient) RegisterOAuthClient(registerUrl string, audience []string) ([]byte, error) {
// hydra endpoint: POST /clients
audience = QuoteArrayStrings(audience)
data := []byte(fmt.Sprintf(`{
"client_name": "%s",
"token_endpoint_auth_method": "client_secret_post",
"scope": "openid email profile",
"grant_types": ["client_credentials", "urn:ietf:params:oauth:grant-type:jwt-bearer"],
"response_types": ["token"]
}`, client.Id))

req, err := http.NewRequest("POST", registerUrl, bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("failed to make request: %v", err)
}
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to do request: %v", err)
}
defer res.Body.Close()

return io.ReadAll(res.Body)
}

func (client *OAuthClient) AuthorizeClient(authorizeUrl string) ([]byte, error) {
// encode ID and secret for authorization header basic authentication
basicAuth := base64.StdEncoding.EncodeToString(
[]byte(fmt.Sprintf("%s:%s",
url.QueryEscape(client.Id),
url.QueryEscape(client.Secret),
)),
)
body := []byte("grant_type=client_credentials&scope=read")
headers := map[string][]string{
"Authorization": {basicAuth},
"Content-Type": {"application/x-www-form-urlencoded"},
}

req, err := http.NewRequest("POST", authorizeUrl, bytes.NewBuffer(body))
req.Header = headers
if err != nil {
return nil, fmt.Errorf("failed to make request: %v", err)
}
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to do request: %v", err)
}
defer res.Body.Close()

return io.ReadAll(res.Body)
}

func (client *OAuthClient) FetchTokenFromAuthorizationServer(remoteUrl string, scope []string) ([]byte, error) {
// hydra endpoint: /oauth/token
data := "grant_type=" + url.QueryEscape("urn:ietf:params:oauth:grant-type:jwt-bearer") +
"&client_id=" + client.Id +
"&client_secret=" + client.Secret +
"&scope=" + strings.Join(scope, "+")
fmt.Printf("encoded params: %v\n\n", data)
req, err := http.NewRequest("POST", remoteUrl, bytes.NewBuffer([]byte(data)))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
return nil, fmt.Errorf("failed to make request: %s", err)
}
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to do request: %v", err)
}
defer res.Body.Close()

return io.ReadAll(res.Body)
}

func QuoteArrayStrings(arr []string) []string {
for i, v := range arr {
arr[i] = "\"" + v + "\""
}
return arr
}
Loading