Improve JWT tokens expiration
- Reduce access tokens TTL to 15 minutes - Set refresh tokens TTL to 60 days - Set Guardian.DB to only track refresh tokens - Remove refresh token when logging out Closes #710 #705 #706 Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
5a13c2191c
commit
a7da5ab269
|
@ -120,14 +120,19 @@ config :logger, Sentry.LoggerBackend,
|
||||||
level: :warn,
|
level: :warn,
|
||||||
capture_log_messages: true
|
capture_log_messages: true
|
||||||
|
|
||||||
config :mobilizon, Mobilizon.Web.Auth.Guardian, issuer: "mobilizon"
|
config :mobilizon, Mobilizon.Web.Auth.Guardian,
|
||||||
|
issuer: "mobilizon",
|
||||||
|
token_ttl: %{
|
||||||
|
"access" => {15, :minutes},
|
||||||
|
"refresh" => {60, :days}
|
||||||
|
}
|
||||||
|
|
||||||
config :guardian, Guardian.DB,
|
config :guardian, Guardian.DB,
|
||||||
repo: Mobilizon.Storage.Repo,
|
repo: Mobilizon.Storage.Repo,
|
||||||
# default
|
# default
|
||||||
schema_name: "guardian_tokens",
|
schema_name: "guardian_tokens",
|
||||||
# store all token types if not set
|
# store all token types if not set
|
||||||
# token_types: ["refresh_token"],
|
token_types: ["refresh"],
|
||||||
# default: 60 minutes
|
# default: 60 minutes
|
||||||
sweep_interval: 60
|
sweep_interval: 60
|
||||||
|
|
||||||
|
|
|
@ -46,3 +46,9 @@ export const REFRESH_TOKEN = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LOGOUT = gql`
|
||||||
|
mutation Logout($refreshToken: String!) {
|
||||||
|
logout(refreshToken: $refreshToken)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { IPerson } from "@/types/actor";
|
||||||
import { IDENTITIES, UPDATE_CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
|
import { IDENTITIES, UPDATE_CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
|
||||||
import { ICurrentUserRole } from "@/types/enums";
|
import { ICurrentUserRole } from "@/types/enums";
|
||||||
import { NormalizedCacheObject } from "@apollo/client/cache/inmemory/types";
|
import { NormalizedCacheObject } from "@apollo/client/cache/inmemory/types";
|
||||||
|
import { LOGOUT } from "@/graphql/auth";
|
||||||
|
|
||||||
export function saveTokenData(obj: IToken): void {
|
export function saveTokenData(obj: IToken): void {
|
||||||
localStorage.setItem(AUTH_ACCESS_TOKEN, obj.accessToken);
|
localStorage.setItem(AUTH_ACCESS_TOKEN, obj.accessToken);
|
||||||
|
@ -96,6 +97,13 @@ export async function initializeCurrentActor(
|
||||||
export async function logout(
|
export async function logout(
|
||||||
apollo: ApolloClient<NormalizedCacheObject>
|
apollo: ApolloClient<NormalizedCacheObject>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
await apollo.mutate({
|
||||||
|
mutation: LOGOUT,
|
||||||
|
variables: {
|
||||||
|
refreshToken: localStorage.getItem(AUTH_REFRESH_TOKEN),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await apollo.mutate({
|
await apollo.mutate({
|
||||||
mutation: UPDATE_CURRENT_USER_CLIENT,
|
mutation: UPDATE_CURRENT_USER_CLIENT,
|
||||||
variables: {
|
variables: {
|
||||||
|
|
|
@ -385,7 +385,7 @@ export default class Notifications extends Vue {
|
||||||
private async isSubscribed(): Promise<boolean> {
|
private async isSubscribed(): Promise<boolean> {
|
||||||
if (!("serviceWorker" in navigator)) return Promise.resolve(false);
|
if (!("serviceWorker" in navigator)) return Promise.resolve(false);
|
||||||
const registration = await navigator.serviceWorker.getRegistration();
|
const registration = await navigator.serviceWorker.getRegistration();
|
||||||
return (await registration?.pushManager.getSubscription()) !== null;
|
return (await registration?.pushManager.getSubscription()) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteFeedToken(token: string): Promise<void> {
|
private async deleteFeedToken(token: string): Promise<void> {
|
||||||
|
|
|
@ -105,6 +105,28 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
|
||||||
{:error, dgettext("errors", "You need to have an existing token to get a refresh token")}
|
{:error, dgettext("errors", "You need to have an existing token to get a refresh token")}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def logout(_parent, %{refresh_token: refresh_token}, %{context: %{current_user: %User{}}}) do
|
||||||
|
with {:ok, _claims} <- Auth.Guardian.decode_and_verify(refresh_token, %{"typ" => "refresh"}),
|
||||||
|
{:ok, _claims} <- Auth.Guardian.revoke(refresh_token) do
|
||||||
|
{:ok, refresh_token}
|
||||||
|
else
|
||||||
|
{:error, :token_not_found} ->
|
||||||
|
{:error, :token_not_found}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
Logger.debug("Cannot remove user refresh token: #{inspect(error)}")
|
||||||
|
{:error, :unable_to_logout}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def logout(_parent, %{refresh_token: _refresh_token}, _context) do
|
||||||
|
{:error, :unauthenticated}
|
||||||
|
end
|
||||||
|
|
||||||
|
def logout(_parent, _params, _context) do
|
||||||
|
{:error, :invalid_argument}
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Register an user:
|
Register an user:
|
||||||
- check registrations are enabled
|
- check registrations are enabled
|
||||||
|
|
|
@ -310,6 +310,12 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
resolve(&User.refresh_token/3)
|
resolve(&User.refresh_token/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@desc "Logout an user, deleting a refresh token"
|
||||||
|
field :logout, :string do
|
||||||
|
arg(:refresh_token, non_null(:string))
|
||||||
|
resolve(&User.logout/3)
|
||||||
|
end
|
||||||
|
|
||||||
@desc "Change default actor for user"
|
@desc "Change default actor for user"
|
||||||
field :change_default_actor, :user do
|
field :change_default_actor, :user do
|
||||||
arg(:preferred_username, non_null(:string), description: "The actor preferred_username")
|
arg(:preferred_username, non_null(:string), description: "The actor preferred_username")
|
||||||
|
|
Loading…
Reference in a new issue