From d45f5df4dc5fa9ad8abdc47c940f6baf96fdbe45 Mon Sep 17 00:00:00 2001 From: Jeroen Wijenbergh Date: Mon, 21 Mar 2022 11:20:34 +0100 Subject: OAuth: Make sure the tokens are not expired --- src/api.go | 11 +++++++++-- src/oauth.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/api.go b/src/api.go index 43e92d7..d485728 100644 --- a/src/api.go +++ b/src/api.go @@ -4,8 +4,15 @@ import ( "net/http" ) -func (eduvpn *VPNState) APIAuthenticatedInfo() (string, error) { - url := eduvpn.Server.Endpoints.API.V3.API + "/info" +func (eduvpn *VPNState) APIAuthenticatedGet(endpoint string) (string, error) { + url := eduvpn.Server.Endpoints.API.V3.API + endpoint + + // Ensure we have non-expired tokens + oauthErr := eduvpn.EnsureTokensOAuth() + + if oauthErr != nil { + return "", oauthErr + } headers := &http.Header{"Authorization": {"Bearer " + eduvpn.Server.OAuth.Token.Access}} body, bodyErr := HTTPGetWithOptionalParams(url, &HTTPOptionalParams{Headers: headers}) diff --git a/src/oauth.go b/src/oauth.go index 80f60d7..bbe34af 100644 --- a/src/oauth.go +++ b/src/oauth.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/url" + "time" ) // Generates a random base64 string to be used for state @@ -73,12 +74,18 @@ type OAuthExchangeSession struct { Server *http.Server } +func generateTimeSeconds() int64 { + current := time.Now() + return current.Unix() +} + // Struct that defines the json format for /.well-known/vpn-user-portal" type OAuthToken struct { Access string `json:"access_token"` Refresh string `json:"refresh_token"` Type string `json:"token_type"` - Expires int `json:"expires_in"` + Expires int64 `json:"expires_in"` + ExpiredTimestamp int64 } // Gets an authenticated HTTP client by obtaining refresh and access tokens @@ -115,6 +122,7 @@ func (oauth *OAuth) getTokensWithAuthCode(authCode string) error { headers := &http.Header{ "content-type": {"application/x-www-form-urlencoded"}} opts := &HTTPOptionalParams{Headers: headers} + current_time := generateTimeSeconds() body, bodyErr := HTTPPostWithOptionalParams(reqURL, data, opts) if bodyErr != nil { return bodyErr @@ -127,11 +135,18 @@ func (oauth *OAuth) getTokensWithAuthCode(authCode string) error { return &HTTPParseJsonError{URL: reqURL, Body: string(body), Err: jsonErr} } + tokenStructure.ExpiredTimestamp = current_time + tokenStructure.Expires oauth.Token = tokenStructure return nil } +func (oauth *OAuth) isTokensExpired() bool { + expired_time := oauth.Token.ExpiredTimestamp + current_time := generateTimeSeconds() + return current_time >= expired_time +} + // Get the access and refresh tokens with a previously received refresh token // Access tokens: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-04#section-1.4 // Refresh tokens: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-04#section-1.3.2 @@ -144,6 +159,7 @@ func (oauth *OAuth) getTokensWithRefresh() error { headers := &http.Header{ "content-type": {"application/x-www-form-urlencoded"}} opts := &HTTPOptionalParams{Headers: headers} + current_time := generateTimeSeconds() body, bodyErr := HTTPPostWithOptionalParams(reqURL, data, opts) if bodyErr != nil { return bodyErr @@ -156,6 +172,7 @@ func (oauth *OAuth) getTokensWithRefresh() error { return &HTTPParseJsonError{URL: reqURL, Body: string(body), Err: jsonErr} } + tokenStructure.ExpiredTimestamp = current_time + tokenStructure.Expires oauth.Token = tokenStructure return nil @@ -253,6 +270,19 @@ func (eduvpn *VPNState) FinishOAuth() error { return oauth.getTokensWithCallback() } +func (eduvpn *VPNState) EnsureTokensOAuth() error { + oauth := eduvpn.Server.OAuth + if oauth == nil { + panic("invalid oauth state") + } + + if oauth.isTokensExpired() { + return oauth.getTokensWithRefresh(); + } + return nil +} + + type OAuthGenStateUnableError struct { Err error } -- cgit v1.2.3