satellite/console: use cookie based auth scheme

Change-Id: I143b56f49fa9028ec172db8c29fd93577c3e7878
This commit is contained in:
Yaroslav 2020-01-20 20:57:14 +02:00
parent b678b55f83
commit c636b06191
17 changed files with 160 additions and 174 deletions

View File

@ -23,14 +23,16 @@ import (
func newConsoleEndpoints(address string) *consoleEndpoints {
return &consoleEndpoints{
client: http.DefaultClient,
base: "http://" + address,
client: http.DefaultClient,
base: "http://" + address,
cookieName: "_tokenKey",
}
}
type consoleEndpoints struct {
client *http.Client
base string
client *http.Client
base string
cookieName string
}
func (ce *consoleEndpoints) appendPath(suffix string) string {
@ -309,8 +311,12 @@ func (ce *consoleEndpoints) getProject(token string) (string, error) {
q.Add("query", `query {myProjects{id}}`)
request.URL.RawQuery = q.Encode()
request.AddCookie(&http.Cookie{
Name: ce.cookieName,
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
var getProjects struct {
MyProjects []struct {
@ -341,8 +347,12 @@ func (ce *consoleEndpoints) createProject(token string) (string, error) {
return "", errs.Wrap(err)
}
request.AddCookie(&http.Cookie{
Name: ce.cookieName,
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
var createProject struct {
CreateProject struct {
@ -370,8 +380,12 @@ func (ce *consoleEndpoints) createAPIKey(token, projectID string) (string, error
return "", errs.Wrap(err)
}
request.AddCookie(&http.Cookie{
Name: ce.cookieName,
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
var createAPIKey struct {
CreateAPIKey struct {

View File

@ -15,6 +15,7 @@ import (
"storj.io/storj/private/post"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleweb/consoleql"
"storj.io/storj/satellite/console/consoleweb/consolewebauth"
"storj.io/storj/satellite/mailservice"
)
@ -24,24 +25,26 @@ var ErrAuthAPI = errs.Class("console auth api error")
// Auth is an api controller that exposes all auth functionality.
type Auth struct {
log *zap.Logger
service *console.Service
mailService *mailservice.Service
ExternalAddress string
LetUsKnowURL string
TermsAndConditionsURL string
ContactInfoURL string
service *console.Service
mailService *mailservice.Service
cookieAuth *consolewebauth.CookieAuth
}
// NewAuth is a constructor for api auth controller.
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, externalAddress string, letUsKnowURL string, termsAndConditionsURL string, contactInfoURL string) *Auth {
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, cookieAuth *consolewebauth.CookieAuth, externalAddress string, letUsKnowURL string, termsAndConditionsURL string, contactInfoURL string) *Auth {
return &Auth{
log: log,
service: service,
mailService: mailService,
ExternalAddress: externalAddress,
LetUsKnowURL: letUsKnowURL,
TermsAndConditionsURL: termsAndConditionsURL,
ContactInfoURL: contactInfoURL,
service: service,
mailService: mailService,
cookieAuth: cookieAuth,
}
}
@ -68,6 +71,8 @@ func (a *Auth) Token(w http.ResponseWriter, r *http.Request) {
return
}
a.cookieAuth.SetTokenCookie(w, token)
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(token)
if err != nil {

View File

@ -0,0 +1,50 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package consolewebauth
import (
"net/http"
"time"
)
// CookieSettings variable cookie settings.
type CookieSettings struct {
Name string
Path string
}
// CookieAuth handles cookie authorization.
type CookieAuth struct {
settings CookieSettings
}
// NewCookieAuth create new cookie authorization with provided settings.
func NewCookieAuth(settings CookieSettings) *CookieAuth {
return &CookieAuth{
settings: settings,
}
}
// GetToken retrieves token from request.
func (auth *CookieAuth) GetToken(r *http.Request) (string, error) {
cookie, err := r.Cookie(auth.settings.Name)
if err != nil {
return "", err
}
return cookie.Value, nil
}
// SetTokenCookie sets parametrized token cookie that is not accessible from js.
func (auth *CookieAuth) SetTokenCookie(w http.ResponseWriter, token string) {
http.SetCookie(w, &http.Cookie{
Name: auth.settings.Name,
Value: token,
Path: auth.settings.Path,
// TODO: get expiration from token
Expires: time.Now().Add(time.Hour * 24),
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
})
}

View File

@ -32,15 +32,13 @@ import (
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleweb/consoleapi"
"storj.io/storj/satellite/console/consoleweb/consoleql"
"storj.io/storj/satellite/console/consoleweb/consolewebauth"
"storj.io/storj/satellite/mailservice"
"storj.io/storj/satellite/referrals"
)
const (
authorization = "Authorization"
contentType = "Content-Type"
authorizationBearer = "Bearer "
contentType = "Content-Type"
applicationJSON = "application/json"
applicationGraphql = "application/graphql"
@ -86,8 +84,9 @@ type Server struct {
mailService *mailservice.Service
referralsService *referrals.Service
listener net.Listener
server http.Server
listener net.Listener
server http.Server
cookieAuth *consolewebauth.CookieAuth
stripePublicKey string
@ -117,6 +116,11 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
logger.Sugar().Debugf("Starting Satellite UI on %s...", server.listener.Addr().String())
server.cookieAuth = consolewebauth.NewCookieAuth(consolewebauth.CookieSettings{
Name: "_tokenKey",
Path: "/",
})
if server.config.ExternalAddress != "" {
if !strings.HasSuffix(server.config.ExternalAddress, "/") {
server.config.ExternalAddress += "/"
@ -128,11 +132,12 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
router := mux.NewRouter()
fs := http.FileServer(http.Dir(server.config.StaticDir))
router.HandleFunc("/api/v0/graphql", server.grapqlHandler)
router.HandleFunc("/registrationToken/", server.createRegistrationTokenHandler)
router.HandleFunc("/populate-promotional-coupons", server.populatePromotionalCoupons).Methods(http.MethodPost)
router.HandleFunc("/robots.txt", server.seoHandler)
router.Handle("/api/v0/graphql", server.withAuth(http.HandlerFunc(server.grapqlHandler)))
router.Handle(
"/api/v0/projects/{id}/usage-limits",
server.withAuth(http.HandlerFunc(server.projectUsageLimitsHandler)),
@ -143,7 +148,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
referralsRouter.Handle("/tokens", server.withAuth(http.HandlerFunc(referralsController.GetTokens))).Methods(http.MethodGet)
referralsRouter.HandleFunc("/register", referralsController.Register).Methods(http.MethodPost)
authController := consoleapi.NewAuth(logger, service, mailService, server.config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
authController := consoleapi.NewAuth(logger, service, mailService, server.cookieAuth, server.config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
authRouter := router.PathPrefix("/api/v0/auth").Subrouter()
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.GetAccount))).Methods(http.MethodGet)
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.UpdateAccount))).Methods(http.MethodPatch)
@ -255,19 +260,29 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
// authMiddlewareHandler performs initial authorization before every request.
func (server *Server) withAuth(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
token := getToken(r)
var ctx context.Context
ctx = auth.WithAPIKey(ctx, []byte(token))
auth, err := server.service.Authorize(ctx)
if err != nil {
ctx = console.WithAuthFailure(ctx, err)
} else {
ctx = console.WithAuth(ctx, auth)
defer mon.Task()(&ctx)(&err)
ctxWithAuth := func(ctx context.Context) context.Context {
token, err := server.cookieAuth.GetToken(r)
if err != nil {
return console.WithAuthFailure(ctx, err)
}
ctx = auth.WithAPIKey(ctx, []byte(token))
auth, err := server.service.Authorize(ctx)
if err != nil {
return console.WithAuthFailure(ctx, err)
}
return console.WithAuth(ctx, auth)
}
ctx = ctxWithAuth(r.Context())
handler.ServeHTTP(w, r.Clone(ctx))
})
}
@ -278,13 +293,13 @@ func (server *Server) bucketUsageReportHandler(w http.ResponseWriter, r *http.Re
var err error
defer mon.Task()(&ctx)(&err)
tokenCookie, err := r.Cookie("_tokenKey")
token, err := server.cookieAuth.GetToken(r)
if err != nil {
server.serveError(w, http.StatusUnauthorized)
return
}
auth, err := server.service.Authorize(auth.WithAPIKey(ctx, []byte(tokenCookie.Value)))
auth, err := server.service.Authorize(auth.WithAPIKey(ctx, []byte(token)))
if err != nil {
server.serveError(w, http.StatusUnauthorized)
return
@ -376,34 +391,39 @@ func (server *Server) createRegistrationTokenHandler(w http.ResponseWriter, r *h
// populatePromotionalCoupons is web app http handler function for populating promotional coupons.
func (server *Server) populatePromotionalCoupons(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
defer mon.Task()(&ctx)(nil)
w.Header().Set(contentType, applicationJSON)
var err error
var ctx context.Context
var response struct {
Error string `json:"error,omitempty"`
}
defer mon.Task()(&ctx)(&err)
defer func() {
err := json.NewEncoder(w).Encode(&response)
if err != nil {
handleError := func(status int, err error) {
w.WriteHeader(status)
w.Header().Set(contentType, applicationJSON)
var response struct {
Error string `json:"error"`
}
response.Error = err.Error()
if err := json.NewEncoder(w).Encode(response); err != nil {
server.log.Error("failed to write json error response", zap.Error(Error.Wrap(err)))
}
}()
}
ctx = r.Context()
equality := subtle.ConstantTimeCompare(
[]byte(r.Header.Get("Authorization")),
[]byte(server.config.AuthToken),
)
if equality != 1 {
w.WriteHeader(401)
response.Error = "unauthorized"
handleError(http.StatusUnauthorized, errs.New("unauthorized"))
return
}
err := server.service.Payments().PopulatePromotionalCoupons(ctx)
if err != nil {
response.Error = err.Error()
if err = server.service.Payments().PopulatePromotionalCoupons(ctx); err != nil {
handleError(http.StatusInternalServerError, err)
return
}
}
@ -580,16 +600,6 @@ func (server *Server) grapqlHandler(w http.ResponseWriter, r *http.Request) {
return
}
token := getToken(r)
ctx = auth.WithAPIKey(ctx, []byte(token))
auth, err := server.service.Authorize(ctx)
if err != nil {
ctx = console.WithAuthFailure(ctx, err)
} else {
ctx = console.WithAuth(ctx, auth)
}
rootObject := make(map[string]interface{})
rootObject["origin"] = server.config.ExternalAddress

View File

@ -8,7 +8,6 @@ import (
"io/ioutil"
"mime"
"net/http"
"strings"
"github.com/zeebo/errs"
@ -38,20 +37,6 @@ type graphqlJSON struct {
Variables map[string]interface{}
}
// getToken retrieves token from request
func getToken(req *http.Request) string {
value := req.Header.Get(authorization)
if value == "" {
return ""
}
if !strings.HasPrefix(value, authorizationBearer) {
return ""
}
return value[len(authorizationBearer):]
}
// getQuery retrieves graphql query from request
func getQuery(w http.ResponseWriter, req *http.Request) (query graphqlJSON, err error) {
switch req.Method {

View File

@ -22,7 +22,7 @@ export class AuthHttpApi {
*/
public async resendEmail(userId: string): Promise<void> {
const path = `${this.ROOT_PATH}/resend-email/${userId}`;
const response = await this.http.post(path, userId, false);
const response = await this.http.post(path, userId);
if (response.ok) {
return;
}
@ -43,7 +43,7 @@ export class AuthHttpApi {
email: email,
password: password,
};
const response = await this.http.post(path, JSON.stringify(body), false);
const response = await this.http.post(path, JSON.stringify(body));
if (response.ok) {
return await response.json();
}
@ -63,7 +63,7 @@ export class AuthHttpApi {
*/
public async forgotPassword(email: string): Promise<void> {
const path = `${this.ROOT_PATH}/forgot-password/${email}`;
await this.http.post(path, email, false);
await this.http.post(path, email);
}
/**
@ -78,7 +78,7 @@ export class AuthHttpApi {
fullName: userInfo.fullName,
shortName: userInfo.shortName,
};
const response = await this.http.patch(path, JSON.stringify(body), true);
const response = await this.http.patch(path, JSON.stringify(body));
if (response.ok) {
return;
}
@ -93,7 +93,7 @@ export class AuthHttpApi {
*/
public async get(): Promise<User> {
const path = `${this.ROOT_PATH}/account`;
const response = await this.http.get(path, true);
const response = await this.http.get(path);
if (response.ok) {
return await response.json();
}
@ -114,7 +114,7 @@ export class AuthHttpApi {
password: password,
newPassword: newPassword,
};
const response = await this.http.post(path, JSON.stringify(body), true);
const response = await this.http.post(path, JSON.stringify(body));
if (response.ok) {
return;
}
@ -140,7 +140,7 @@ export class AuthHttpApi {
const body = {
password: password,
};
const response = await this.http.post(path, JSON.stringify(body), true);
const response = await this.http.post(path, JSON.stringify(body));
if (response.ok) {
return;
}
@ -174,7 +174,7 @@ export class AuthHttpApi {
partnerId: user.partnerId ? user.partnerId : '',
};
const response = await this.http.post(path, JSON.stringify(body), false);
const response = await this.http.post(path, JSON.stringify(body));
if (!response.ok) {
if (response.status === 401) {
throw new ErrorUnauthorized('we are unable to create your account. This is an invite-only alpha, please join our waitlist to receive an invitation');
@ -204,7 +204,7 @@ export class AuthHttpApi {
email: user.email,
};
const response = await this.http.post(path, JSON.stringify(body), false);
const response = await this.http.post(path, JSON.stringify(body));
if (!response.ok) {
if (response.status === 400) {
throw new Error('we are unable to create your account. This is an invite-only alpha, please join our waitlist to receive an invitation');

View File

@ -121,7 +121,7 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
*/
public async getLimits(projectId): Promise<ProjectLimits> {
const path = `${this.ROOT_PATH}/${projectId}/usage-limits`;
const response = await this.http.get(path, true);
const response = await this.http.get(path);
if (response.ok) {
const limits = await response.json();

View File

@ -19,7 +19,7 @@ export class ReferralHttpApi {
*/
public async getTokens(): Promise<string[]> {
const path = `${this.ROOT_PATH}/tokens`;
const response = await this.http.get(path, true);
const response = await this.http.get(path);
if (response.ok) {
return await response.json();

View File

@ -53,7 +53,6 @@ import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/router';
import { AuthToken } from '@/utils/authToken';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { validatePassword } from '@/utils/validation';
@ -97,12 +96,11 @@ export default class DeleteAccountPopup extends Vue {
try {
await this.auth.delete(this.password);
await this.$notify.success('Account was successfully deleted');
this.$segment.track(SegmentEvent.USER_DELETED, {
email: this.$store.getters.user.email,
});
AuthToken.remove();
this.isLoading = false;
await this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_DEL_ACCOUNT);
await this.$router.push(RouteConfig.Login.path);

View File

@ -30,7 +30,6 @@ import { RouteConfig } from '@/router';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { AuthToken } from '@/utils/authToken';
import {
API_KEYS_ACTIONS,
APP_STATE_ACTIONS,
@ -56,8 +55,6 @@ export default class AccountDropdown extends Vue {
}
public onLogoutClick(): void {
AuthToken.remove();
this.$router.push(RouteConfig.Login.path);
this.$store.dispatch(PM_ACTIONS.CLEAR);
this.$store.dispatch(PROJECTS_ACTIONS.CLEAR);

View File

@ -19,7 +19,6 @@ import ProjectMembersArea from '@/components/team/ProjectMembersArea.vue';
import store from '@/store';
import { NavigationLink } from '@/types/navigation';
import { AuthToken } from '@/utils/authToken';
const DashboardArea = () => import('@/views/DashboardArea.vue');
const ForgotPassword = () => import('@/views/forgotPassword/ForgotPassword.vue');
const LoginArea = () => import('@/views/login/LoginArea.vue');
@ -163,14 +162,6 @@ export const router = new Router({
});
router.beforeEach((to, from, next) => {
if (to.matched.some(route => route.meta.requiresAuth)) {
if (!AuthToken.get()) {
next(RouteConfig.Login.path);
return;
}
}
if (navigateToDefaultSubTab(to.matched, RouteConfig.Account)) {
next(RouteConfig.Account.with(RouteConfig.Profile).path);

View File

@ -6,23 +6,17 @@ import ApolloClient from 'apollo-client/ApolloClient';
import { setContext } from 'apollo-link-context';
import { HttpLink } from 'apollo-link-http';
import { AuthToken } from '@/utils/authToken';
// Satellite url
const satelliteUrl = new HttpLink({
uri: process.env.VUE_APP_ENDPOINT_URL,
});
// Adding auth headers
// Adding additional headers
const authLink = setContext((_, {headers}) => {
// get the authentication token from local storage if it exists
const token = AuthToken.get();
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});

View File

@ -1,39 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
// AuthToken exposes methods to manage auth cookie
export class AuthToken {
private static tokenKey: string = '_tokenKey';
public static get(): string {
return AuthToken.getCookie(AuthToken.tokenKey);
}
public static set(tokenValue: string): void {
document.cookie = AuthToken.tokenKey + '=' + tokenValue + '; path=/';
}
public static remove(): void {
document.cookie = AuthToken.tokenKey + '=; path=/';
}
private static getCookie(cname: string): string {
const name: string = cname + '=';
const decodedCookie: string = decodeURIComponent(document.cookie);
const ca: string[] = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return '';
}
}

View File

@ -1,8 +1,6 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import { AuthToken } from '@/utils/authToken';
/**
* HttpClient is a custom wrapper around fetch api.
* Exposes get, post and delete methods for JSON strings.
@ -15,22 +13,16 @@ export class HttpClient {
* @param body serialized JSON
* @param auth indicates if authentication is needed
*/
private async sendJSON(method: string, path: string, body: string | null, auth: boolean): Promise<Response> {
private async sendJSON(method: string, path: string, body: string | null): Promise<Response> {
const request: RequestInit = {
method: method,
body: body,
};
const headers: Record<string, string> = {
request.headers = {
'Content-Type': 'application/json',
};
if (auth) {
headers['Authorization'] = `Bearer ${AuthToken.get()}`;
}
request.headers = headers;
return await fetch(path, request);
}
@ -40,8 +32,8 @@ export class HttpClient {
* @param body serialized JSON
* @param auth indicates if authentication is needed
*/
public async post(path: string, body: string | null, auth: boolean = true): Promise<Response> {
return this.sendJSON('POST', path, body, auth);
public async post(path: string, body: string | null): Promise<Response> {
return this.sendJSON('POST', path, body);
}
/**
@ -50,8 +42,8 @@ export class HttpClient {
* @param body serialized JSON
* @param auth indicates if authentication is needed
*/
public async patch(path: string, body: string | null, auth: boolean = true): Promise<Response> {
return this.sendJSON('PATCH', path, body, auth);
public async patch(path: string, body: string | null): Promise<Response> {
return this.sendJSON('PATCH', path, body);
}
/**
@ -60,8 +52,8 @@ export class HttpClient {
* @param body serialized JSON
* @param auth indicates if authentication is needed
*/
public async put(path: string, body: string | null, auth: boolean = true): Promise<Response> {
return this.sendJSON('PUT', path, body, auth);
public async put(path: string, body: string | null): Promise<Response> {
return this.sendJSON('PUT', path, body);
}
/**
@ -69,8 +61,8 @@ export class HttpClient {
* @param path
* @param auth indicates if authentication is needed
*/
public async get(path: string, auth: boolean = true): Promise<Response> {
return this.sendJSON('GET', path, null, auth);
public async get(path: string): Promise<Response> {
return this.sendJSON('GET', path, null);
}
/**
@ -78,7 +70,7 @@ export class HttpClient {
* @param path
* @param auth indicates if authentication is needed
*/
public async delete(path: string, auth: boolean = true): Promise<Response> {
return this.sendJSON('DELETE', path, null, auth);
public async delete(path: string): Promise<Response> {
return this.sendJSON('DELETE', path, null);
}
}

View File

@ -43,7 +43,6 @@ import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { PROJECT_USAGE_ACTIONS } from '@/store/modules/usage';
import { USER_ACTIONS } from '@/store/modules/users';
import { Project } from '@/types/projects';
import { AuthToken } from '@/utils/authToken';
import {
API_KEYS_ACTIONS,
APP_STATE_ACTIONS,
@ -76,7 +75,6 @@ export default class DashboardArea extends Vue {
} catch (error) {
await this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.ERROR);
await this.$notify.error(error.message);
AuthToken.remove();
await this.$router.push(RouteConfig.Login.path);
return;
@ -90,7 +88,6 @@ export default class DashboardArea extends Vue {
await this.$store.dispatch(GET_PROJECT_CHARGES);
} catch (error) {
if (error instanceof ErrorUnauthorized) {
AuthToken.remove();
await this.$router.push(RouteConfig.Login.path);
return;

View File

@ -14,7 +14,6 @@ import LoadingLogoIcon from '@/../static/images/LogoWhite.svg';
import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/router';
import { AuthToken } from '@/utils/authToken';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { AppState } from '@/utils/constants/appStateEnum';
@ -78,7 +77,6 @@ export default class Login extends Vue {
try {
this.authToken = await this.auth.token(this.email, this.password);
AuthToken.set(this.authToken);
this.$segment.track(SegmentEvent.USER_LOGGED_IN, {
email: this.email,
});
@ -96,7 +94,6 @@ export default class Login extends Vue {
this.activateLoadingOverlay();
setTimeout(() => {
AuthToken.set(this.authToken);
this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADING);
this.isLoading = false;
this.$router.push(RouteConfig.ProjectOverview.with(RouteConfig.ProjectDetails).path);

View File

@ -14,7 +14,6 @@ import { makeProjectsModule } from '@/store/modules/projects';
import { makeUsageModule } from '@/store/modules/usage';
import { makeUsersModule } from '@/store/modules/users';
import { User } from '@/types/users';
import { AuthToken } from '@/utils/authToken';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { AppState } from '@/utils/constants/appStateEnum';
import { NotificatorPlugin } from '@/utils/plugins/notificator';
@ -98,8 +97,6 @@ describe('Dashboard', () => {
});
it('loads routes correctly when authorithed without project with available routes', async () => {
jest.spyOn(AuthToken, 'get').mockReturnValue('authToken');
const availableWithoutProject = [
RouteConfig.Account.with(RouteConfig.Billing).path,
RouteConfig.Account.with(RouteConfig.Profile).path,
@ -119,8 +116,6 @@ describe('Dashboard', () => {
});
it('loads routes correctly when authorithed without project with unavailable routes', async () => {
jest.spyOn(AuthToken, 'get').mockReturnValue('authToken');
const unavailableWithoutProject = [
RouteConfig.ApiKeys.path,
RouteConfig.Buckets.path,