Skip to content

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Conversation

christiannwamba
Copy link
Contributor

Related Issue or Design Document

Checklist

  • I have read the contributing guidelines and signed the CLA.
  • I have referenced an issue containing the design document if my change introduces a new feature.
  • I have read the security policy.
  • I confirm that this pull request does not address a security vulnerability.
    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.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added the necessary documentation within the code base (if appropriate).

Further comments

Copy link
Collaborator

@piotrmsc piotrmsc left a 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.

Copy link
Member

@aeneasr aeneasr left a 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

Copy link
Member

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Comment on lines +111 to +115
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>
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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>
Copy link
Member

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.

Suggested change
<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>
Copy link
Member

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

Suggested change
<CodeBlock language="go" title={'main.go'}> {goLogin} </CodeBlock>
<CodeBlock language="go" title={'login.go'}> {goLogin} </CodeBlock>

```mdx-code-block
</TabItem>
</Tabs>
```
Copy link
Member

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:

  1. Now run the server using ...
  2. Open https://localhost:3000/login (or whatever link is needed)
  3. You shuold see X, click here do that
  4. Done

```

```mdx-code-block
</TabItem>
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants