Merge pull request 'Added "last active at" field to OAuth tokens for easier management' (#8061) from token-activity into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8061
This commit is contained in:
HJ 2026-11-09 19:06:06 +00:00
commit 7d6c533ac0
6 changed files with 61 additions and 7 deletions

View file

@ -0,0 +1 @@
Added "last active at" field to OAuth token management

View file

@ -21,6 +21,7 @@ defmodule Pleroma.Web.OAuth.Token do
field(:refresh_token, :string)
field(:scopes, {:array, :string}, default: [])
field(:valid_until, :naive_datetime_usec)
field(:last_active, :naive_datetime_usec)
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:app, App)
@ -96,6 +97,13 @@ defmodule Pleroma.Web.OAuth.Token do
|> validate_required([:valid_until])
end
@spec update_activity(Token.t()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()}
def update_activity(token) do
token
|> Ecto.Changeset.change(last_active: NaiveDateTime.utc_now())
|> Repo.update()
end
@spec create(App.t(), User.t(), map()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()}
def create(%App{} = app, %User{} = user, attrs \\ %{}) do
with {:ok, token} <- do_create(app, user, attrs) do

View file

@ -17,6 +17,7 @@ defmodule Pleroma.Web.OAuth.TokenView do
%{
id: token_entry.id,
valid_until: token_entry.valid_until,
last_active: token_entry.last_active,
app_name: token_entry.app.client_name,
scopes: token_entry.scopes,
is_current_token: token_entry.id == current_token.id

View file

@ -16,6 +16,8 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
@update_interval :timer.hours(1)
def init(options), do: options
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
@ -24,16 +26,32 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
with {:ok, token_str} <- fetch_token_str(conn) do
with {:ok, user, user_token} <- fetch_user_and_token(token_str),
false <- Token.expired?(user_token) do
conn
|> assign(:token, user_token)
|> assign(:user, user)
with true <- needs_update?(user_token),
{:ok, updated_token} <- Token.update_activity(user_token) do
conn
|> assign(:token, updated_token)
|> assign(:user, user)
else
_ ->
conn
|> assign(:token, user_token)
|> assign(:user, user)
end
else
_ ->
with {:ok, app, app_token} <- fetch_app_and_token(token_str),
false <- Token.expired?(app_token) do
conn
|> assign(:token, app_token)
|> assign(:app, app)
with true <- needs_update?(app_token),
{:ok, updated_token} <- Token.update_activity(app_token) do
conn
|> assign(:token, updated_token)
|> assign(:app, app)
else
_ ->
conn
|> assign(:token, app_token)
|> assign(:app, app)
end
else
_ -> conn
end
@ -43,6 +61,12 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
end
end
defp needs_update?(%Token{last_active: nil}), do: true
defp needs_update?(%Token{last_active: last_active}) do
NaiveDateTime.diff(NaiveDateTime.utc_now(), last_active, :millisecond) >= @update_interval
end
# Gets user by token
#
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil

View file

@ -0,0 +1,13 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo.Migrations.AddTokenActivity do
use Ecto.Migration
def change do
alter table(:oauth_tokens) do
add_if_not_exists(:last_active, :naive_datetime_usec)
end
end
end

View file

@ -63,7 +63,14 @@ defmodule Pleroma.Web.OAuth.TokenControllerTest do
|> Map.keys()
assert Enum.sort(keys) ==
Enum.sort(["id", "app_name", "valid_until", "scopes", "is_current_token"])
Enum.sort([
"id",
"app_name",
"valid_until",
"scopes",
"is_current_token",
"last_active"
])
end
test "marks which token is being used for the request" do