Skip to content

Commit

Permalink
feat: Redirect to requested URL after SSO login (argoproj#4495)
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Behar <[email protected]>
  • Loading branch information
simster7 committed Nov 10, 2020
1 parent 465447c commit 0931baf
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 17 deletions.
22 changes: 10 additions & 12 deletions server/auth/sso/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,12 @@ func newSso(
}, nil
}

const stateCookieName = "oauthState"

func (s *sso) HandleRedirect(w http.ResponseWriter, r *http.Request) {
redirectUrl := r.URL.Query().Get("redirect")
state := pkgrand.RandString(10)
http.SetCookie(w, &http.Cookie{
Name: stateCookieName,
Value: state,
Name: state,
Value: redirectUrl,
Expires: time.Now().Add(3 * time.Minute),
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Expand All @@ -195,18 +194,13 @@ func (s *sso) HandleRedirect(w http.ResponseWriter, r *http.Request) {
func (s *sso) HandleCallback(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
state := r.URL.Query().Get("state")
cookie, err := r.Cookie(stateCookieName)
http.SetCookie(w, &http.Cookie{Name: stateCookieName, MaxAge: 0})
cookie, err := r.Cookie(state)
http.SetCookie(w, &http.Cookie{Name: state, MaxAge: 0})
if err != nil {
w.WriteHeader(400)
_, _ = w.Write([]byte(fmt.Sprintf("invalid state: %v", err)))
return
}
if state != cookie.Value {
w.WriteHeader(401)
_, _ = w.Write([]byte("invalid state: does not match cookie value"))
return
}
oauth2Token, err := s.config.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
w.WriteHeader(401)
Expand Down Expand Up @@ -246,7 +240,11 @@ func (s *sso) HandleCallback(w http.ResponseWriter, r *http.Request) {
SameSite: http.SameSiteStrictMode,
Secure: s.secure,
})
http.Redirect(w, r, s.baseHRef, 302)
redirect := s.baseHRef
if cookie.Value != "" {
redirect = cookie.Value
}
http.Redirect(w, r, redirect, 302)
}

// authorize verifies a bearer token and pulls user information form the claims.
Expand Down
15 changes: 13 additions & 2 deletions ui/src/app/login/components/login.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Page} from 'argo-ui';
import * as React from 'react';
import {uiUrl} from '../../shared/base';
import {uiUrl, uiUrlWithParams} from '../../shared/base';

require('./login.scss');

Expand All @@ -13,6 +13,13 @@ const user = (token: string) => {
document.cookie = 'authorization=' + token + ';SameSite=Strict;path=' + path;
document.location.href = path;
};
const getRedirect = (): string => {
const urlParams = new URLSearchParams(new URL(document.location.href).search);
if (urlParams.has('redirect')) {
return 'redirect=' + urlParams.get('redirect');
}
return '';
};
export const Login = () => (
<Page title='Login' toolbar={{breadcrumbs: [{title: 'Login'}]}}>
<div className='argo-container'>
Expand All @@ -32,7 +39,11 @@ export const Login = () => (
If your organisation has configured <b>single sign-on</b>:
</p>
<div>
<button className='argo-button argo-button--base-o' onClick={() => (document.location.href = uiUrl('oauth2/redirect'))}>
<button
className='argo-button argo-button--base-o'
onClick={() => {
document.location.href = uiUrlWithParams('oauth2/redirect', [getRedirect()]);
}}>
<i className='fa fa-sign-in-alt' /> Login
</button>
</div>
Expand Down
7 changes: 7 additions & 0 deletions ui/src/app/shared/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ export function uiUrl(uiPath: string): string {
return baseUrl() + uiPath;
}

export function uiUrlWithParams(uiPath: string, params: string[]): string {
if (!params) {
return uiUrl(uiPath);
}
return baseUrl() + uiPath + '?' + params.join('&');
}

export function apiUrl(apiPath: string): string {
return `${baseUrl()}${apiPath}`;
}
6 changes: 3 additions & 3 deletions ui/src/app/shared/services/requests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Observable, Observer} from 'rxjs';
import * as _superagent from 'superagent';
import {SuperAgentRequest} from 'superagent';
import {apiUrl, uiUrl} from '../base';
import {apiUrl, uiUrlWithParams} from '../base';

const superagentPromise = require('superagent-promise');

Expand All @@ -11,8 +11,8 @@ const auth = (req: SuperAgentRequest) => {

const handle = (err: any) => {
// check URL to prevent redirect loop
if (err.status === 401 && !document.location.href.endsWith('login')) {
document.location.href = uiUrl('login');
if (err.status === 401 && !document.location.href.includes('login')) {
document.location.href = uiUrlWithParams('login', ['redirect=' + document.location.href]);
}
};

Expand Down

0 comments on commit 0931baf

Please sign in to comment.