Skip to content

Commit

Permalink
Merge pull request #59 from supabase/feature/redirect_to
Browse files Browse the repository at this point in the history
Adds a "redirectTo" option to all relevant functions

fix: #62
  • Loading branch information
kiwicopple committed Feb 22, 2021
2 parents 66a58ae + aa3ed98 commit 6332b08
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 37 deletions.
5 changes: 5 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Releases are handled by Semantic release. This document is for forcing and documenting any non-code changes.

### v1.11.0

- Feature: https://github.com/supabase/gotrue-js/issues/62 Give the ability for developers to redirect their users to a specified URL after they are logged in.


### v1.10.1

- Fix https://github.com/supabase/gotrue-js/issues/38
Expand Down
2 changes: 1 addition & 1 deletion example/react/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function App() {
}, [])

async function handleOAuthLogin(provider) {
let { error } = await auth.signIn({ provider })
let { error } = await auth.signIn({ provider }, { redirectTo: 'http://localhost:3000/welcome'})
if (error) console.log('Error: ', error.message)
}
async function handleEmailSignIn() {
Expand Down
18 changes: 9 additions & 9 deletions infra/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ services:
GOTRUE_JWT_EXP: 3600
GOTRUE_DB_DRIVER: postgres
DB_NAMESPACE: auth
API_EXTERNAL_URL: http://localhost
GOTRUE_API_HOST: 0.0.0.0
PORT: 9999
GOTRUE_DISABLE_SIGNUP: "false"
API_EXTERNAL_URL: http://localhost:9999
GOTRUE_SITE_URL: http://localhost:9999
GOTRUE_MAILER_AUTOCONFIRM: "false"
GOTRUE_LOG_LEVEL: DEBUG
Expand All @@ -34,17 +34,17 @@ services:
autoconfirm: # Signup enabled, autoconfirm on
image: supabase/gotrue:latest
ports:
- '9998:9999'
- '9998:9998'
environment:
GOTRUE_JWT_SECRET: "37c304f8-51aa-419a-a1af-06154e63707a"
GOTRUE_JWT_EXP: 3600
GOTRUE_DB_DRIVER: postgres
DB_NAMESPACE: auth
API_EXTERNAL_URL: http://localhost:9998
GOTRUE_API_HOST: 0.0.0.0
PORT: 9999
PORT: 9998
GOTRUE_DISABLE_SIGNUP: "false"
GOTRUE_SITE_URL: http://localhost
API_EXTERNAL_URL: http://localhost:9998
GOTRUE_SITE_URL: http://localhost:9998
GOTRUE_MAILER_AUTOCONFIRM: "true"
GOTRUE_LOG_LEVEL: DEBUG
GOTRUE_OPERATOR_TOKEN: super-secret-operator-token
Expand All @@ -54,17 +54,17 @@ services:
disabled: # Signup disabled
image: supabase/gotrue:latest
ports:
- '9997:9999'
- '9997:9997'
environment:
GOTRUE_JWT_SECRET: "37c304f8-51aa-419a-a1af-06154e63707a"
GOTRUE_JWT_EXP: 3600
GOTRUE_DB_DRIVER: postgres
DB_NAMESPACE: auth
API_EXTERNAL_URL: http://localhost:9997
GOTRUE_API_HOST: 0.0.0.0
PORT: 9999
PORT: 9997
GOTRUE_DISABLE_SIGNUP: "true"
GOTRUE_SITE_URL: http://localhost
API_EXTERNAL_URL: http://localhost:9997
GOTRUE_SITE_URL: http://localhost:9997
GOTRUE_MAILER_AUTOCONFIRM: "false"
GOTRUE_LOG_LEVEL: DEBUG
GOTRUE_OPERATOR_TOKEN: super-secret-operator-token
Expand Down
56 changes: 48 additions & 8 deletions src/GoTrueApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,24 @@ export default class GoTrueApi {
* Creates a new user using their email address.
* @param email The email address of the user.
* @param password The password of the user.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*
* @returns A logged-in session if the server has "autoconfirm" ON
* @returns A user if the server has "autoconfirm" OFF
*/
async signUpWithEmail(
email: string,
password: string
password: string,
options: {
redirectTo?: string
} = {}
): Promise<{ data: Session | User | null; error: Error | null }> {
try {
const data = await post(`${this.url}/signup`, { email, password }, { headers: this.headers })
let headers = { ...this.headers }
if (options.redirectTo) {
headers['referer'] = options.redirectTo
}
const data = await post(`${this.url}/signup`, { email, password }, { headers })
return { data, error: null }
} catch (error) {
return { data: null, error }
Expand All @@ -50,10 +58,14 @@ export default class GoTrueApi {
* Logs in an existing user using their email address.
* @param email The email address of the user.
* @param password The password of the user.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async signInWithEmail(
email: string,
password: string
password: string,
options: {
redirectTo?: string
} = {}
): Promise<{ data: Session | null; error: Error | null }> {
try {
const data = await post(
Expand All @@ -70,8 +82,14 @@ export default class GoTrueApi {
/**
* Sends a magic login link to an email address.
* @param email The email address of the user.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async sendMagicLinkEmail(email: string): Promise<{ data: {} | null; error: Error | null }> {
async sendMagicLinkEmail(
email: string,
options: {
redirectTo?: string
} = {}
): Promise<{ data: {} | null; error: Error | null }> {
try {
const data = await post(`${this.url}/magiclink`, { email }, { headers: this.headers })
return { data, error: null }
Expand All @@ -83,8 +101,14 @@ export default class GoTrueApi {
/**
* Sends an invite link to an email address.
* @param email The email address of the user.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async inviteUserByEmail(email: string): Promise<{ data: {} | null; error: Error | null }> {
async inviteUserByEmail(
email: string,
options: {
redirectTo?: string
} = {}
): Promise<{ data: {} | null; error: Error | null }> {
try {
const data = await post(`${this.url}/invite`, { email }, { headers: this.headers })
return { data, error: null }
Expand All @@ -96,8 +120,14 @@ export default class GoTrueApi {
/**
* Sends a reset request to an email address.
* @param email The email address of the user.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async resetPasswordForEmail(email: string): Promise<{ data: {} | null; error: Error | null }> {
async resetPasswordForEmail(
email: string,
options: {
redirectTo?: string
} = {}
): Promise<{ data: {} | null; error: Error | null }> {
try {
const data = await post(`${this.url}/recover`, { email }, { headers: this.headers })
return { data, error: null }
Expand Down Expand Up @@ -137,9 +167,19 @@ export default class GoTrueApi {
/**
* Generates the relevant login URL for a third-party provider.
* @param provider One of the providers supported by GoTrue.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
getUrlForProvider(provider: Provider) {
return `${this.url}/authorize?provider=${provider}`
getUrlForProvider(
provider: Provider,
options: {
redirectTo?: string
}
) {
let urlParams: string[] = [`provider=${provider}`]
if (options?.redirectTo) {
urlParams.push(`redirect_to=${options.redirectTo}`)
}
return `${this.url}/authorize?${urlParams.join('&')}`
}

/**
Expand Down
64 changes: 47 additions & 17 deletions src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ export default class GoTrueClient {
* @type UserCredentials
* @param email The user's email address.
* @param password The user's password.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async signUp({
email,
password,
}: UserCredentials): Promise<{
async signUp(
{ email, password }: UserCredentials,
options: {
redirectTo?: string
} = {}
): Promise<{
user: User | null
session: Session | null
error: Error | null
Expand All @@ -101,7 +104,9 @@ export default class GoTrueClient {
try {
this._removeSession()

const { data, error } = await this.api.signUpWithEmail(email!, password!)
const { data, error } = await this.api.signUpWithEmail(email!, password!, {
redirectTo: options.redirectTo,
})

if (error) {
throw error
Expand Down Expand Up @@ -137,12 +142,14 @@ export default class GoTrueClient {
* @param email The user's email address.
* @param password The user's password.
* @param provider One of the providers supported by GoTrue.
* @param redirectTo A URL or mobile address to send the user to after they are confirmed.
*/
async signIn({
email,
password,
provider,
}: UserCredentials): Promise<{
async signIn(
{ email, password, provider }: UserCredentials,
options: {
redirectTo?: string
} = {}
): Promise<{
session: Session | null
user: User | null
provider?: Provider
Expand All @@ -154,11 +161,21 @@ export default class GoTrueClient {
this._removeSession()

if (email && !password) {
const { error } = await this.api.sendMagicLinkEmail(email)
const { error } = await this.api.sendMagicLinkEmail(email, {
redirectTo: options.redirectTo,
})
return { data: null, user: null, session: null, error }
}
if (email && password) return this._handleEmailSignIn(email, password)
if (provider) return this._handleProviderSignIn(provider)
if (email && password) {
return this._handleEmailSignIn(email, password, {
redirectTo: options.redirectTo,
})
}
if (provider) {
return this._handleProviderSignIn(provider, {
redirectTo: options.redirectTo,
})
}
throw new Error(`You must provide either an email or a third-party provider.`)
} catch (error) {
return { data: null, user: null, session: null, error }
Expand Down Expand Up @@ -316,9 +333,17 @@ export default class GoTrueClient {
}
}

private async _handleEmailSignIn(email: string, password: string) {
private async _handleEmailSignIn(
email: string,
password: string,
options: {
redirectTo?: string
} = {}
) {
try {
const { data, error } = await this.api.signInWithEmail(email, password)
const { data, error } = await this.api.signInWithEmail(email, password, {
redirectTo: options.redirectTo,
})
if (error || !data) return { data: null, user: null, session: null, error }

if (data?.user?.confirmed_at) {
Expand All @@ -332,8 +357,13 @@ export default class GoTrueClient {
}
}

private _handleProviderSignIn(provider: Provider) {
const url: string = this.api.getUrlForProvider(provider)
private _handleProviderSignIn(
provider: Provider,
options: {
redirectTo?: string
} = {}
) {
const url: string = this.api.getUrlForProvider(provider, { redirectTo: options.redirectTo })

try {
// try to open on the browser
Expand Down
2 changes: 2 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ export interface UserCredentials {
password?: string
// (Optional) The name of the provider.
provider?: Provider
/** A URL to redirect the user to after confirmation. */
redirectTo?: URL
}
4 changes: 4 additions & 0 deletions test/__snapshots__/provider.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
exports[`signIn() with Provider 1`] = `"http://localhost:9999/authorize?provider=google"`;

exports[`signIn() with Provider 2`] = `"google"`;

exports[`signIn() with Provider can append a redirectUrl 1`] = `"http://localhost:9999/authorize?provider=google&redirect_to=https://localhost:9000/welcome"`;

exports[`signIn() with Provider can append a redirectUrl 2`] = `"google"`;
7 changes: 5 additions & 2 deletions test/apiWithAutoConfirmDisabled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ const api = new GoTrueApi({
url: GOTRUE_URL,
})

const email = `API_AC_DISABLED_${faker.internet.email()}`
const email = `api_ac_disabled_${faker.internet.email()}`
const password = faker.internet.password()

test('signUpWithEmail()', async () => {
let { error, data } = await api.signUpWithEmail(email, password)
let { error, data } = await api.signUpWithEmail(email, password, {
redirectTo: 'https://localhost:9999/welcome',
})
expect(error).toBeNull()
expect(data).toMatchSnapshot({
id: expect.any(String),
confirmation_sent_at: expect.any(String),
Expand Down
14 changes: 14 additions & 0 deletions test/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ test('signIn() with Provider', async () => {
expect(url).toMatchSnapshot()
expect(provider).toMatchSnapshot()
})

test('signIn() with Provider can append a redirectUrl ', async () => {
let { error, url, provider } = await auth.signIn(
{
provider: 'google',
},
{
redirectTo: 'https://localhost:9000/welcome',
}
)
expect(error).toBeNull()
expect(url).toMatchSnapshot()
expect(provider).toMatchSnapshot()
})

0 comments on commit 6332b08

Please sign in to comment.