summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/main.go78
-rw-r--r--src/api.go30
-rw-r--r--src/oauth.go93
-rw-r--r--src/state.go24
4 files changed, 130 insertions, 95 deletions
diff --git a/cli/main.go b/cli/main.go
index fe7dc1d..4d334ac 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -3,10 +3,7 @@ package main
import (
"flag"
eduvpn "github.com/jwijenbergh/eduvpn-common/src"
- "golang.org/x/oauth2"
- "io/ioutil"
"log"
- "net/http"
"os/exec"
"strings"
)
@@ -17,64 +14,6 @@ func openBrowser(urlString string) {
exec.Command("xdg-open", urlString).Start()
}
-func constructConfig(urlString string) (*oauth2.Config, string) {
- // Get the endpoints
- endpoints, err := eduvpn.APIGetEndpoints(urlString)
- if err != nil {
- log.Fatal("Error API: cannot get endpoints. Message: ", err)
- }
- log.Printf("API: Got endpoints:\n- V3 API %s\n- V3 Authorization %s\n- V3 Token %s\n", endpoints.API.V3.Endpoint, endpoints.API.V3.AuthorizationEndpoint, endpoints.API.V3.TokenEndpoint)
-
- // Start the OAuth procedure
- config := &oauth2.Config{
- RedirectURL: "http://127.0.0.1:8000/callback",
- ClientID: "org.eduvpn.app.linux",
- Scopes: []string{"config"},
- Endpoint: oauth2.Endpoint{
- AuthURL: endpoints.API.V3.AuthorizationEndpoint,
- TokenURL: endpoints.API.V3.TokenEndpoint,
- },
- }
- return config, endpoints.API.V3.Endpoint
-}
-
-func auth(urlString string) (*http.Client, string) {
- // Get the config
- oauthConfig, apiString := constructConfig(urlString)
-
- // Initialize oauth with the config
- eduOAuth, err := eduvpn.InitializeOAuth(oauthConfig)
- if err != nil {
- log.Fatal("Error OAuth: cannot initialize OAuth. Message: ", err)
- }
-
- // Open the browser
- openBrowser(eduOAuth.AuthURL)
-
- // Get and return authenticated client
- client, err := eduOAuth.GetHTTPTokenClient()
- if err != nil {
- log.Fatal("Error OAUth: cannot get authenticated HTTP client. Message: ", err)
- }
- return client, apiString
-}
-
-func show_info(client *http.Client, apiString string) {
- log.Println("OAUth: Got authenticated HTTP client")
-
- resp, err := client.Get(apiString + "/info")
- if err != nil {
- panic(err)
- }
- defer resp.Body.Close()
-
- bodyBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- panic(err)
- }
- log.Println(string(bodyBytes))
-}
-
func main() {
urlArg := flag.String("url", "", "The url of the vpn")
flag.Parse()
@@ -89,6 +28,19 @@ func main() {
urlString = "https://" + urlString
}
- client, apiString := auth(urlString)
- show_info(client, apiString)
+ state := eduvpn.Register("org.eduvpn.app.linux", urlString)
+ authURL, err := eduvpn.InitializeOAuth(state)
+ if err != nil {
+ log.Fatal(err)
+ }
+ openBrowser(authURL)
+ oauthErr := eduvpn.FinishOAuth(state)
+ if oauthErr != nil {
+ log.Fatal(oauthErr)
+ }
+ infoString, infoErr := eduvpn.APIAuthenticatedInfo(state)
+ if infoErr != nil {
+ log.Fatal(infoErr)
+ }
+ log.Println(infoString)
}
diff --git a/src/api.go b/src/api.go
index 9e082cc..8bd2847 100644
--- a/src/api.go
+++ b/src/api.go
@@ -2,6 +2,7 @@ package eduvpn
import (
"encoding/json"
+ "errors"
"io/ioutil"
"net/http"
)
@@ -13,7 +14,7 @@ type endpointList struct {
}
// Struct that defines the json format for /.well-known/vpn-user-portal"
-type PortalEndpoints struct {
+type EduVPNEndpoints struct {
API struct {
V2 endpointList `json:"http://eduvpn.org/api#2"`
V3 endpointList `json:"http://eduvpn.org/api#3"`
@@ -21,8 +22,8 @@ type PortalEndpoints struct {
V string `json:"v"`
}
-func APIGetEndpoints(baseURL string) (*PortalEndpoints, error) {
- url := baseURL + "/.well-known/vpn-user-portal"
+func APIGetEndpoints(vpnState *EduVPNState) (*EduVPNEndpoints, error) {
+ url := vpnState.Server + "/.well-known/vpn-user-portal"
resp, reqErr := http.Get(url)
if reqErr != nil {
return nil, reqErr
@@ -40,7 +41,7 @@ func APIGetEndpoints(baseURL string) (*PortalEndpoints, error) {
return nil, readErr
}
- structure := &PortalEndpoints{}
+ structure := &EduVPNEndpoints{}
jsonErr := json.Unmarshal(body, &structure)
if jsonErr != nil {
@@ -49,3 +50,24 @@ func APIGetEndpoints(baseURL string) (*PortalEndpoints, error) {
return structure, nil
}
+
+func APIAuthenticatedInfo(vpnState *EduVPNState) (string, error) {
+ url := vpnState.Endpoints.API.V3.Endpoint + "/info"
+ resp, reqErr := vpnState.OAuth.client.Get(url)
+ if reqErr != nil {
+ return "", reqErr
+ }
+ // Close the response body at the end
+ defer resp.Body.Close()
+
+ // Check if http response code is ok
+ if resp.StatusCode != http.StatusOK {
+ return "", errors.New("HTTP code not ok")
+ }
+ // Read the body
+ body, readErr := ioutil.ReadAll(resp.Body)
+ if readErr != nil {
+ return "", readErr
+ }
+ return string(body), nil
+}
diff --git a/src/oauth.go b/src/oauth.go
index 9eb2272..87284a2 100644
--- a/src/oauth.go
+++ b/src/oauth.go
@@ -66,33 +66,8 @@ type EduVPNOauth struct {
verifier string
}
-// Initializes the OAuth eduvpn class. It returns a tuple of the class and error.
-// If the error is non-nil, the class will be nil.
-func InitializeOAuth(config *oauth2.Config) (*EduVPNOauth, error) {
- // Generate the state
- state, stateErr := genState()
- if stateErr != nil {
- return nil, detailedOAuthError{errGenStateError, fmt.Sprintf("oauth failed to gen random bytes for state"), stateErr}
- }
-
- // Generate the verifier and challenge
- verifier, err := genVerifier()
- if err != nil {
- return nil, detailedOAuthError{errGenVerifierError, fmt.Sprintf("oauth failed to verifier"), err}
- }
- challenge := genChallengeS256(verifier)
-
- // Update the auth url with the challenge and state
- codeChallengeMethod := oauth2.SetAuthURLParam("code_challenge_method", "S256")
- codeChallenge := oauth2.SetAuthURLParam("code_challenge", challenge)
- authURL := config.AuthCodeURL(state, codeChallengeMethod, codeChallenge)
-
- // Return the struct with the necessary fields filled for the next call to getting the HTTP client
- return &EduVPNOauth{AuthURL: authURL, Config: config, state: state, verifier: verifier}, nil
-}
-
// Gets an authenticated HTTP client by obtaining refresh and access tokens
-func (eduvpn *EduVPNOauth) GetHTTPTokenClient() (*http.Client, error) {
+func (eduvpn *EduVPNOauth) getHTTPTokenClient() error {
eduvpn.context = context.Background()
mux := http.NewServeMux()
eduvpn.server = &http.Server{
@@ -101,9 +76,9 @@ func (eduvpn *EduVPNOauth) GetHTTPTokenClient() (*http.Client, error) {
}
mux.HandleFunc("/callback", eduvpn.oauthCallback)
if err := eduvpn.server.ListenAndServe(); err != http.ErrServerClosed {
- return nil, detailedOAuthError{errCallbackServerError, fmt.Sprintf("oauth callback server error"), err}
+ return detailedOAuthError{errCallbackServerError, fmt.Sprintf("oauth callback server error"), err}
}
- return eduvpn.client, eduvpn.callbackError
+ return eduvpn.callbackError
}
// Get the access and refresh tokens
@@ -169,6 +144,68 @@ func (eduvpn *EduVPNOauth) oauthCallback(w http.ResponseWriter, req *http.Reques
go eduvpn.server.Shutdown(eduvpn.context)
}
+// Generate a config for oauth
+// It uses the state to get the server and the name
+func genConfig(vpnState *EduVPNState) (*oauth2.Config, error) {
+ config := &oauth2.Config{
+ RedirectURL: "http://127.0.0.1:8000/callback",
+ ClientID: vpnState.Name,
+ Scopes: []string{"config"},
+ Endpoint: oauth2.Endpoint{
+ AuthURL: vpnState.Endpoints.API.V3.AuthorizationEndpoint,
+ TokenURL: vpnState.Endpoints.API.V3.TokenEndpoint,
+ },
+ }
+ return config, nil
+}
+
+// Initializes the OAuth for eduvpn.
+// It needs a vpn state that was gotten from `Register`
+// It returns the authurl for the browser and an error if present
+func InitializeOAuth(vpnState *EduVPNState) (string, error) {
+ if vpnState == nil {
+ panic("invalid state")
+ }
+
+ config, configErr := genConfig(vpnState)
+ if configErr != nil {
+ return "", configErr
+ }
+
+ // Generate the state
+ state, stateErr := genState()
+ if stateErr != nil {
+ return "", detailedOAuthError{errGenStateError, fmt.Sprintf("oauth failed to gen random bytes for state"), stateErr}
+ }
+
+ // Generate the verifier and challenge
+ verifier, err := genVerifier()
+ if err != nil {
+ return "", detailedOAuthError{errGenVerifierError, fmt.Sprintf("oauth failed to verifier"), err}
+ }
+ challenge := genChallengeS256(verifier)
+
+ // Update the auth url with the challenge and state
+ codeChallengeMethod := oauth2.SetAuthURLParam("code_challenge_method", "S256")
+ codeChallenge := oauth2.SetAuthURLParam("code_challenge", challenge)
+ authURL := config.AuthCodeURL(state, codeChallengeMethod, codeChallenge)
+
+ // Fill the struct with the necessary fields filled for the next call to getting the HTTP client
+ vpnState.OAuth = &EduVPNOauth{AuthURL: authURL, Config: config, state: state, verifier: verifier}
+ return authURL, nil
+}
+
+func FinishOAuth(vpnState *EduVPNState) error {
+ if vpnState == nil {
+ panic("invalid state")
+ }
+
+ if vpnState.OAuth == nil {
+ panic("invalid oauth state")
+ }
+ return vpnState.OAuth.getHTTPTokenClient()
+}
+
// OAuthErrorCode Simplified error code for public interface.
type OAuthErrorCode = VPNErrorCode
type OAuthError = VPNError
diff --git a/src/state.go b/src/state.go
new file mode 100644
index 0000000..a03733a
--- /dev/null
+++ b/src/state.go
@@ -0,0 +1,24 @@
+package eduvpn
+
+type EduVPNState struct {
+ // The struct used for oauth
+ OAuth *EduVPNOauth
+
+ // The endpoints
+ Endpoints *EduVPNEndpoints
+
+ // Info passed by the client
+ Name string
+ Server string
+}
+
+func Register(name string, server string) *EduVPNState {
+ state := &EduVPNState{Name: name, Server: server}
+ endpoints, err := APIGetEndpoints(state)
+
+ if err != nil {
+ panic(err)
+ }
+ state.Endpoints = endpoints
+ return state
+}