-
Notifications
You must be signed in to change notification settings - Fork 0
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
OAuth Integration #891
Merged
Merged
OAuth Integration #891
Changes from 9 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2ede462
Fix package.json
45ae090
Merge branch 'main' of https://github.com/GenerateNU/sac
26bc2f4
Merge branch 'main' of https://github.com/GenerateNU/sac
444e26e
Merge branch 'main' of https://github.com/GenerateNU/sac
65da53f
Merge branch 'main' of https://github.com/GenerateNU/sac
1eb8554
Merge branch 'main' of https://github.com/GenerateNU/sac
0671d4d
Merge branch 'main' of https://github.com/GenerateNU/sac
daefa31
Fully Functioning OAuth For Google
a6ee639
Merge branch 'main' into oauth-integration
garrettladley 96b90ee
Merge branch 'main' into oauth-integration
garrettladley bd60ef0
resolve pr comments | UserIDFrom fiber.Ctx helper
garrettladley 56e7a21
lint | format | move request body
garrettladley 29e49c1
track file
garrettladley 1187dc9
soc
garrettladley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package config | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
|
||
m "github.com/garrettladley/mattress" | ||
) | ||
|
||
type OAuthSettings struct { | ||
BaseURL string | ||
TokenURL string | ||
ClientID *m.Secret[string] | ||
ClientSecret *m.Secret[string] | ||
Scopes string | ||
RedirectURI string | ||
ResponseType string | ||
ResponseMode string | ||
AccessType string | ||
IncludeGrantedScopes string | ||
Prompt string | ||
} | ||
|
||
type OAuthResources struct { | ||
GoogleOAuthSettings *OAuthSettings | ||
OutlookOAuthSettings *OAuthSettings | ||
} | ||
|
||
/** | ||
**/ | ||
func readGoogleOAuthSettings() (*OAuthSettings, error) { | ||
clientID := os.Getenv("GOOGLE_OAUTH_CLIENT_ID") | ||
if clientID == "" { | ||
return nil, errors.New("GOOGLE_OAUTH_CLIENT_ID is not set") | ||
} | ||
|
||
secretClientID, err := m.NewSecret(clientID) | ||
if err != nil { | ||
return nil, errors.New("failed to create secret from client ID") | ||
} | ||
|
||
clientSecret := os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET") | ||
if clientSecret == "" { | ||
return nil, errors.New("GOOGLE_OAUTH_CLIENT_SECRET is not set") | ||
} | ||
|
||
secretClientSecret, err := m.NewSecret(clientSecret) | ||
if err != nil { | ||
return nil, errors.New("failed to create secret from client secret") | ||
} | ||
|
||
return &OAuthSettings{ | ||
BaseURL: "https://accounts.google.com/o/oauth2/v2", | ||
TokenURL: "https://oauth2.googleapis.com", | ||
ClientID: secretClientID, | ||
ClientSecret: secretClientSecret, | ||
Scopes: "https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly", | ||
ResponseType: "code", | ||
RedirectURI: "http://localhost:3000", | ||
IncludeGrantedScopes: "true", | ||
AccessType: "offline", | ||
Prompt: "consent", | ||
}, nil | ||
} | ||
|
||
/** | ||
* OUTLOOK | ||
**/ | ||
func readOutlookOAuthSettings() (*OAuthSettings, error) { | ||
clientID := os.Getenv("OUTLOOK_OAUTH_CLIENT_ID") | ||
if clientID == "" { | ||
return nil, errors.New("OUTLOOK_OAUTH_CLIENT_ID is not set") | ||
} | ||
|
||
secretClientID, err := m.NewSecret(clientID) | ||
if err != nil { | ||
return nil, errors.New("failed to create secret from client ID") | ||
} | ||
|
||
clientSecret := os.Getenv("OUTLOOK_OAUTH_CLIENT_SECRET") | ||
if clientSecret == "" { | ||
return nil, errors.New("OUTLOOK_OAUTH_CLIENT_SECRET is not set") | ||
} | ||
|
||
secretClientSecret, err := m.NewSecret(clientSecret) | ||
if err != nil { | ||
return nil, errors.New("failed to create secret from client secret") | ||
} | ||
|
||
return &OAuthSettings{ | ||
BaseURL: "https://login.microsoftonline.com/common/oauth2/v2.0", | ||
TokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0", | ||
ClientID: secretClientID, | ||
ClientSecret: secretClientSecret, | ||
Scopes: "offline_access user.read calendars.readwrite", | ||
ResponseType: "code", | ||
RedirectURI: "http://localhost:3000", | ||
ResponseMode: "query", | ||
Prompt: "consent", | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package models | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
) | ||
|
||
type OAuthResource string | ||
|
||
const ( | ||
Google OAuthResource = "google" | ||
Outlook OAuthResource = "outlook" | ||
) | ||
|
||
type OAuthTokenRequestBody struct { | ||
Code string `json:"code" validate:"omitempty"` | ||
State string `json:"state" validate:"omitempty"` | ||
} | ||
|
||
type UserOAuthTokens struct { | ||
UserID uuid.UUID `json:"user_id" validate:"required,uuid4"` | ||
RefreshToken string `json:"refresh_token" validate:"max=255"` | ||
AccessToken string `json:"access_token" validate:"max=255"` | ||
CSRFToken string `json:"csrf_token" validate:"max=255"` | ||
ResourceType OAuthResource `json:"resource_type" validate:"required"` | ||
ExpiresAt time.Time `json:"expires_at" validate:"required"` | ||
} | ||
|
||
type OAuthToken struct { | ||
AccessToken string `json:"access_token" validate:"required"` | ||
ExpiresIn int `json:"expires_in" validate:"required"` | ||
RefreshToken string `json:"refresh_token" validate:"required"` | ||
Scope string `json:"scope" validate:"required"` | ||
TokenType string `json:"token_type" validate:"required"` | ||
} | ||
|
||
func (UserOAuthTokens) TableName() string { | ||
return "user_oauth_tokens" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package base | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
|
||
"github.com/GenerateNU/sac/backend/entities/models" | ||
"github.com/GenerateNU/sac/backend/utilities" | ||
"github.com/gofiber/fiber/v2" | ||
) | ||
|
||
type OAuthController struct { | ||
OAuthService OAuthServiceInterface | ||
} | ||
|
||
func NewOAuthController(oauthService OAuthServiceInterface) *OAuthController { | ||
return &OAuthController{OAuthService: oauthService} | ||
} | ||
|
||
func (oc *OAuthController) Authorize(c *fiber.Ctx) error { | ||
// Extract the resource type from the query params: | ||
resourceType := models.OAuthResource(c.Query("type")) | ||
if resourceType == "" { | ||
return errors.New("resource type is required") | ||
} | ||
|
||
// Extract the user making the call: | ||
userID := c.Locals("userID").(string) | ||
idAsUUID, err := utilities.ValidateID(userID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Call the respective authorize method: | ||
authUrl, err := oc.OAuthService.Authorize(*idAsUUID, resourceType) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.Status(fiber.StatusOK).JSON(*authUrl) | ||
} | ||
|
||
func (oc *OAuthController) Token(c *fiber.Ctx) (error) { | ||
// Extract the resource type from the query params: | ||
resourceType := models.OAuthResource(c.Query("type")) | ||
if resourceType == "" { | ||
return errors.New("resource type is required") | ||
} | ||
|
||
// Parse the body of the request: | ||
var tokenBody models.OAuthTokenRequestBody | ||
if err := c.BodyParser(&tokenBody); err != nil { | ||
return err | ||
} | ||
|
||
// Extract the user making the call: | ||
userID := c.Locals("userID").(string) | ||
idAsUUID, err := utilities.ValidateID(userID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Call the respective token method: | ||
err = oc.OAuthService.Token(*idAsUUID, tokenBody, resourceType) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.SendStatus(http.StatusNoContent) | ||
} | ||
|
||
func (oc *OAuthController) Revoke(c *fiber.Ctx) error { | ||
// Extract the resource type from the query params: | ||
resourceType := models.OAuthResource(c.Query("type")) | ||
if resourceType == "" { | ||
return errors.New("resource type is required") | ||
} | ||
|
||
// Extract the user making the call: | ||
userID := c.Locals("userID").(string) | ||
idAsUUID, err := utilities.ValidateID(userID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = oc.OAuthService.Revoke(*idAsUUID, resourceType) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.Status(fiber.StatusOK).JSON("Successfully revoked token") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package base | ||
|
||
import "github.com/GenerateNU/sac/backend/types" | ||
|
||
func OAuth(params types.RouteParams) { | ||
oauthController := NewOAuthController(&OAuthService{ ServiceParams: params.ServiceParams }) | ||
|
||
// api/v1/calendar/* | ||
calendar := params.Router.Group("/oauth") | ||
|
||
calendar.Get("/authorize", params.AuthMiddleware.UserAuthorizeAndExtractID, oauthController.Authorize) | ||
calendar.Post("/token", params.AuthMiddleware.UserAuthorizeAndExtractID, oauthController.Token) | ||
calendar.Delete("/revoke", params.AuthMiddleware.UserAuthorizeAndExtractID, oauthController.Revoke) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should use a private type as the key to avoid any potential collisions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, isn't this stored in the custom claims as the issuer? it is set here when logging in