-
Notifications
You must be signed in to change notification settings - Fork 407
docs: oauth getting started #2129
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
base: master
Are you sure you want to change the base?
Conversation
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.
Please fix for consistency "oauth2" -> "OAuth2" as you are mixing both in the documents.
Please remove the "Start" section from navigation, put the information about libraries somewhere else - Get Started maybe?
Please check code example for correctness.
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.
Thank you! Overall I like the get started guide as it goes through the steps to set up oauth2 in your app, very nice!
Unfortunately the Go guide I could not get working due to a missing main function.
I found it frustrating that the tabs always went back to "node js" when switching through the pages. So I selected "go" on configure client, then went to "request authorization code" an had to select "go" again (because it was node now).
All other remarks are in individual comments!
|
||
1. You want to implement OAuth2 authorization in your applications | ||
2. You want to quickly setup Ory's OAuth2 server | ||
|
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.
There are more use cases here:
- Customer is migrating from Auth0 or another OAuth2/OpenID Connect-based authentication solution
- Customer is using a standard library for auth that uses OAuth2 / OpenID Connect (NextAuth, Auth.js, Passport.js, ...)
sidebar_label: Get started | ||
--- | ||
|
||
This guide covers the essential setup and implementation details for adding OAuth2 authorization flows to your application. |
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.
This guide covers the essential setup and implementation details for adding OAuth2 authorization flows to your application. | |
This guide covers the essential setup and implementation details for adding OAuth2 & OpenID Connect flows to your application. |
export ORY_PROJECT_SLUG=<your-project-slug> | ||
export OAUTH_CLIENT_ID=<your-client-id> | ||
export OAUTH_CLIENT_SECRET=<your-client-secret> | ||
export OAUTH_REDIRECT_URI=<your-redirect-uri> # e.g. http://localhost:3000/callback | ||
export SESSION_SECRET=<your-session-secret> |
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.
As a first time reader, this confused me a bit why I need to do this, and where to get the value from, specifically values for:
- session srecret
- redirect uri
Having read the rest of the guide I understand that it's essentially one guide on multiple pages, so this here is then needed later.
--- | ||
id: social-sign-in | ||
title: Configure social sign-in with Ory Identities | ||
sidebar_label: Social sign-in with Ory Identities |
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.
This is a nice gimmick thing that can be set up, but I'm wondering if it should live in "Get Started" or in some advanced guides, as I can't really imagine a use case where this makes sense?
--- | ||
|
||
# Try common OAuth2 Grants |
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.
With the new content, this page probably needs to be refreshed or changed? Maybe move it under get started and adjust it to have "Test client credentials grant" and "Test code grant" or something like that where the user, after setting up their project and the client, can check what the flow looks like without code?
**1. Install the Go OAuth2 package** | ||
|
||
```shell | ||
go get golang.org/x/oauth2 |
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.
If I do this in an empty directory, it will fail because go.mod is missing:
(base) aeneas@ORY-MJF4DHQN0Q go % go get golang.org/x/oauth2
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
I have to do go mod init <module-name>
to init the module
``` | ||
|
||
```mdx-code-block | ||
<CodeBlock language="go" title={'main.go'}> {goCallback} </CodeBlock> |
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.
Consider another file here too, e.g.
<CodeBlock language="go" title={'main.go'}> {goCallback} </CodeBlock> | |
<CodeBlock language="go" title={'callback.go'}> {goCallback} </CodeBlock> |
``` | ||
|
||
```mdx-code-block | ||
<CodeBlock language="go" title={'main.go'}> {goLogin} </CodeBlock> |
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.
Consider putting this into another file, as main.go already has content from the "configure client" step
<CodeBlock language="go" title={'main.go'}> {goLogin} </CodeBlock> | |
<CodeBlock language="go" title={'login.go'}> {goLogin} </CodeBlock> |
```mdx-code-block | ||
</TabItem> | ||
</Tabs> | ||
``` |
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.
The guide just ends abruptly, and the reader is left to figure out what they need to do now to start the server and test the example. I think this should be part of the guide, like:
- Now run the server using ...
- Open https://localhost:3000/login (or whatever link is needed)
- You shuold see X, click here do that
- Done
``` | ||
|
||
```mdx-code-block | ||
</TabItem> |
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.
I tried running the go example and copy pasted all content into a main.go file:
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"net/http"
"crypto/rand"
"encoding/base64"
"time"
"golang.org/x/oauth2"
)
// Configuration
var (
// Replace these with your own values
clientID = os.Getenv("OAUTH_CLIENT_ID")
clientSecret = os.Getenv("OAUTH_CLIENT_SECRET")
projectSlug = os.Getenv("ORY_PROJECT_SLUG")
redirectURL = os.Getenv("OAUTH_REDIRECT_URI")
port = "3000"
// Ory OAuth2 endpoints
oryEndpoint = oauth2.Endpoint{
AuthURL: fmt.Sprintf("https://%s.projects.oryapis.com/oauth2/auth", projectSlug),
TokenURL: fmt.Sprintf("https://%s.projects.oryapis.com/oauth2/token", projectSlug),
}
// OAuth2 config
oauthConfig = &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: redirectURL,
Scopes: []string{"openid", "offline_access", "email"},
Endpoint: oryEndpoint,
}
// In-memory session store (replace with a proper session store in production)
sessions = make(map[string]Session)
)
// Session represents user session data
type Session struct {
State string
CodeVerifier string
Token *oauth2.Token
UserInfo map[string]interface{}
}
func handleLogin(w http.ResponseWriter, r *http.Request) {
// Generate random state parameter
state, err := generateRandomString(32)
if err != nil {
http.Error(w, "Failed to generate state parameter", http.StatusInternalServerError)
return
}
// Generate code verifier for PKCE
codeVerifier := oauth2.GenerateVerifier()
// Create a new session
sessionID, err := generateRandomString(32)
if err != nil {
http.Error(w, "Failed to generate session ID", http.StatusInternalServerError)
return
}
// Store session
sessions[sessionID] = Session{
State: state,
CodeVerifier: codeVerifier,
}
// Set session cookie
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: r.TLS != nil,
MaxAge: int(24 * time.Hour.Seconds()),
})
// Generate authorization URL with PKCE challenge
authURL := oauthConfig.AuthCodeURL(
state,
oauth2.S256ChallengeOption(codeVerifier),
)
// Redirect to authorization server
http.Redirect(w, r, authURL, http.StatusSeeOther)
}
func generateRandomString(length int) (string, error) {
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(b), nil
}
func handleCallback(w http.ResponseWriter, r *http.Request) {
// Get session cookie
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "No session found", http.StatusBadRequest)
return
}
// Get session from store
session, ok := sessions[cookie.Value]
if !ok {
http.Error(w, "Invalid session", http.StatusBadRequest)
return
}
// Get authorization code and state from query parameters
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")
if code == "" || state == "" || state != session.State {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
// Exchange authorization code for token
token, err := oauthConfig.Exchange(
context.Background(),
code,
oauth2.VerifierOption(session.CodeVerifier),
)
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
// Update session with token
session.Token = token
sessions[cookie.Value] = session
// Fetch user info
client := oauthConfig.Client(context.Background(), token)
userInfoURL := fmt.Sprintf("https://%s.projects.oryapis.com/userinfo", projectSlug)
resp, err := client.Get(userInfoURL)
if err != nil {
http.Error(w, "Failed to fetch user info: "+err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// Parse user info response
var userInfo map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
http.Error(w, "Failed to parse user info: "+err.Error(), http.StatusInternalServerError)
return
}
// Update session with user info
session.UserInfo = userInfo
sessions[cookie.Value] = session
// Redirect to home page
http.Redirect(w, r, "/", http.StatusSeeOther)
}
Now, running go run .
it fails because the program's main
routine is not defined:
(base) aeneas@ORY-MJF4DHQN0Q go % go run .
# github.com/ory/docs/go-example
runtime.main_main·f: function main is undeclared in the main package
Related Issue or Design Document
Checklist
If this pull request addresses a security vulnerability,
I confirm that I got approval (please contact security@ory.sh) from the maintainers to push the changes.
Further comments