ampache_react/app/actions/auth.js

254 lines
7.1 KiB
JavaScript

/**
* This file implements authentication related actions.
*/
// NPM imports
import { push } from "react-router-redux";
import Cookies from "js-cookie";
// Local imports
import { buildHMAC, cleanURL } from "../utils";
// Models
import { i18nRecord } from "../models/i18n";
// Other actions and payload types
import { CALL_API } from "../middleware/api";
import { invalidateStore } from "./store";
// Constants
export const DEFAULT_SESSION_INTERVAL = 1800 * 1000; // 30 mins long sessoins by default
/**
* Dispatch a ping query to the API for login keepalive and prevent session
* from expiring.
*
* @param username Username to use
* @param token Token to revive
* @param endpoint Ampache base URL
*
* @return A CALL_API payload to keep session alive.
*/
export function loginKeepAlive(username, token, endpoint) {
return {
type: CALL_API,
payload: {
endpoint: endpoint,
dispatch: [
null,
null,
error => dispatch => {
dispatch(loginUserFailure(error || new i18nRecord({ id: "app.login.expired", values: {}})));
}
],
action: "ping",
auth: token,
username: username,
extraParams: {}
}
};
}
export const LOGIN_USER_SUCCESS = "LOGIN_USER_SUCCESS";
/**
* Action to be called on successful login.
*
* @param username Username used for login
* @param token Token got back from the API
* @param endpoint Ampache server base URL
* @param rememberMe Whether to remember me or not
* @param timerID ID of the timer set for session keepalive.
*
* @return A login success payload.
*/
export function loginUserSuccess(username, token, endpoint, rememberMe, timerID) {
return {
type: LOGIN_USER_SUCCESS,
payload: {
username: username,
token: token,
endpoint: endpoint,
rememberMe: rememberMe,
timerID: timerID
}
};
}
export const LOGIN_USER_FAILURE = "LOGIN_USER_FAILURE";
/**
* Action to be called on failed login.
*
* This action removes any remember me cookie if any was set.
*
* @param error An error object, either string or i18nRecord.
* @return A login failure payload.
*/
export function loginUserFailure(error) {
Cookies.remove("username");
Cookies.remove("token");
Cookies.remove("endpoint");
return {
type: LOGIN_USER_FAILURE,
payload: {
error: error
}
};
}
export const LOGIN_USER_EXPIRED = "LOGIN_USER_EXPIRED";
/**
* Action to be called when session is expired.
*
* @param error An error object, either a string or i18nRecord.
* @return A session expired payload.
*/
export function loginUserExpired(error) {
return {
type: LOGIN_USER_EXPIRED,
payload: {
error: error
}
};
}
export const LOGIN_USER_REQUEST = "LOGIN_USER_REQUEST";
/**
* Action to be called when login is requested.
*
* @return A login request payload.
*/
export function loginUserRequest() {
return {
type: LOGIN_USER_REQUEST
};
}
export const LOGOUT_USER = "LOGOUT_USER";
/**
* Action to be called upon logout.
*
* This function clears the cookies set for remember me and the keep alive
* timer.
*
* @remark This function does not clear the other stores, nor handle
* redirection.
*
* @return A logout payload.
*/
export function logout() {
return (dispatch, state) => {
const { auth } = state();
if (auth.timerID) {
clearInterval(auth.timerID);
}
Cookies.remove("username");
Cookies.remove("token");
Cookies.remove("endpoint");
dispatch({
type: LOGOUT_USER
});
};
}
/**
* Action to be called to log a user out.
*
* This function clears the remember me cookies and the keepalive timer. It
* also clears the data behind authentication in the store and redirects to
* login page.
*/
export function logoutAndRedirect() {
return (dispatch) => {
dispatch(logout());
dispatch(invalidateStore());
dispatch(push("/login"));
};
}
/**
* Action to be called to log a user in.
*
* @param username Username to use.
* @param passwordOrToken User password, or previous token to revive.
* @param endpoint Ampache server base URL.
* @param rememberMe Whether to rememberMe or not
* @param[optional] redirect Page to redirect to after login.
* @param[optional] isToken Whether passwordOrToken is a password or a
* token.
*
* @return A CALL_API payload to perform login.
*/
export function loginUser(username, passwordOrToken, endpoint, rememberMe, redirect="/", isToken=false) {
// Clean endpoint
endpoint = cleanURL(endpoint);
// Get passphrase and time parameters
let time = 0;
let passphrase = passwordOrToken;
if (!isToken) {
// Standard password connection
const HMAC = buildHMAC(passwordOrToken);
time = HMAC.time;
passphrase = HMAC.passphrase;
} else {
// Remember me connection
if (passwordOrToken.expires < new Date()) {
// Token has expired
return loginUserFailure("app.login.expired");
}
time = Math.floor(Date.now() / 1000);
passphrase = passwordOrToken.token;
}
return {
type: CALL_API,
payload: {
endpoint: endpoint,
dispatch: [
loginUserRequest,
jsonData => dispatch => {
if (!jsonData.auth || !jsonData.sessionExpire) {
// On success, check that we are actually authenticated
return dispatch(loginUserFailure(new i18nRecord({ id: "app.api.error", values: {} })));
}
// Get token from the API
const token = {
token: jsonData.auth,
expires: new Date(jsonData.sessionExpire)
};
// Handle session keep alive timer
const timerID = setInterval(
() => dispatch(loginKeepAlive(username, token.token, endpoint)),
DEFAULT_SESSION_INTERVAL
);
if (rememberMe) {
// Handle remember me option
const cookiesOption = { expires: token.expires };
Cookies.set("username", username, cookiesOption);
Cookies.set("token", token, cookiesOption);
Cookies.set("endpoint", endpoint, cookiesOption);
}
// Dispatch login success
dispatch(loginUserSuccess(username, token, endpoint, rememberMe, timerID));
// Redirect
dispatch(push(redirect));
},
loginUserFailure
],
action: "handshake",
auth: passphrase,
username: username,
extraParams: {timestamp: time}
}
};
}