2020-02-05 00:48:55 +01:00
const axios = require ( 'axios' )
2019-07-31 02:01:21 +02:00
const crypto = require ( 'crypto' )
2021-09-27 10:42:17 +02:00
const config = require ( '../config' )
2023-12-20 21:57:30 +01:00
const httpSignature = require ( '@peertube/http-signature' )
2024-01-19 00:42:54 +01:00
const { APUser , Instance , Event } = require ( '../api/models/models' )
const dayjs = require ( 'dayjs' )
2022-12-23 01:08:14 +01:00
2019-09-11 12:00:13 +02:00
const url = require ( 'url' )
2019-09-25 14:38:16 +02:00
const settingsController = require ( '../api/controller/settings' )
2021-03-05 14:17:10 +01:00
const log = require ( '../log' )
2024-01-19 00:42:54 +01:00
const helpers = require ( '../helpers' )
2019-07-30 18:32:26 +02:00
2023-12-28 01:04:16 +01:00
// process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
2019-07-30 18:32:26 +02:00
const Helpers = {
2019-09-13 11:08:18 +02:00
2024-01-19 00:42:54 +01:00
'@context' : [
'https://www.w3.org/ns/activitystreams' ,
'https://w3id.org/security/v1' ,
{
toot : 'http://joinmastodon.org/ns#' ,
// A property-value pair, e.g. representing a feature of a product or place.
// https://docs.joinmastodon.org/spec/activitypub/#PropertyValue
schema : 'http://schema.org#' ,
ProperyValue : 'schema:PropertyValue' ,
value : 'schema:value' ,
// https://docs.joinmastodon.org/spec/activitypub/#discoverable
// This flag may be used as an indicator of the user’ s preferences toward being included
// in external discovery services, such as search engines or other indexing tools
// in gancio is always true
"discoverable" : "toot:discoverable" ,
// https://docs.joinmastodon.org/spec/activitypub/#Hashtag
"Hashtag" : "https://www.w3.org/ns/activitystreams#Hashtag" ,
// supported but always false
manuallyApprovesFollowers : 'as:manuallyApprovesFollowers' ,
// focal point - https://docs.joinmastodon.org/spec/activitypub/#focalPoint
"focalPoint" : {
"@container" : "@list" ,
"@id" : "toot:focalPoint"
}
}
] ,
2019-09-13 11:08:18 +02:00
// ignore unimplemented ping url from fediverse
2019-10-28 17:33:20 +01:00
spamFilter ( req , res , next ) {
2019-09-13 11:08:18 +02:00
const urlToIgnore = [
'/api/v1/instance' ,
'/api/meta' ,
2019-09-18 12:55:33 +02:00
'/api/statusnet/version.json' ,
'/api/gnusocial/version.json' ,
2019-09-13 11:08:18 +02:00
'/api/statusnet/config.json' ,
2020-01-30 15:33:12 +01:00
'/status.php' ,
'/siteinfo.json' ,
'/friendika/json' ,
'/friendica/json' ,
2019-10-28 17:33:20 +01:00
'/poco'
2019-09-13 11:08:18 +02:00
]
2021-03-05 14:17:10 +01:00
if ( urlToIgnore . includes ( req . path ) ) {
log . debug ( ` Ignore noisy fediverse ${ req . path } ` )
return res . status ( 404 ) . send ( 'Not Found' )
}
2019-09-13 11:08:18 +02:00
next ( )
} ,
2023-12-20 21:57:30 +01:00
async signAndSend ( message , inbox , method = 'post' ) {
log . debug ( '[FEDI] Sign and send %s to %s' , message , inbox )
2019-10-30 15:01:15 +01:00
const inboxUrl = new url . URL ( inbox )
2019-12-04 00:50:15 +01:00
const privkey = settingsController . secretSettings . privateKey
2019-07-30 18:32:26 +02:00
const signer = crypto . createSign ( 'sha256' )
2019-07-31 01:43:08 +02:00
const d = new Date ( )
2023-12-20 21:57:30 +01:00
let header
let digest
if ( method === 'post' ) {
digest = crypto . createHash ( 'sha256' )
. update ( message )
. digest ( 'base64' )
const stringToSign = ` (request-target): post ${ inboxUrl . pathname } \n host: ${ inboxUrl . hostname } \n date: ${ d . toUTCString ( ) } \n digest: SHA-256= ${ digest } `
signer . update ( stringToSign )
signer . end ( )
const signature = signer . sign ( privkey )
const signature _b64 = signature . toString ( 'base64' )
header = ` keyId=" ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } #main-key",algorithm="rsa-sha256",headers="(request-target) host date digest",signature=" ${ signature _b64 } " `
} else {
const stringToSign = ` (request-target): get ${ inboxUrl . pathname } \n host: ${ inboxUrl . hostname } \n date: ${ d . toUTCString ( ) } `
signer . update ( stringToSign )
signer . end ( )
const signature = signer . sign ( privkey )
const signature _b64 = signature . toString ( 'base64' )
header = ` keyId=" ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } #main-key",algorithm="rsa-sha256",headers="(request-target) host date",signature=" ${ signature _b64 } " `
}
2020-01-27 00:47:03 +01:00
try {
2020-02-05 00:48:55 +01:00
const ret = await axios ( inbox , {
2020-01-27 00:47:03 +01:00
headers : {
Host : inboxUrl . hostname ,
Date : d . toUTCString ( ) ,
Signature : header ,
2023-12-20 21:57:30 +01:00
... ( method === 'post' && ( { Digest : ` SHA-256= ${ digest } ` } ) ) ,
2023-12-22 14:53:53 +01:00
'Content-Type' : 'application/activity+json' ,
Accept : 'application/activity+json'
2020-01-27 00:47:03 +01:00
} ,
2023-12-20 21:57:30 +01:00
method ,
... ( method === 'post' && ( { data : message } ) )
2020-01-27 00:47:03 +01:00
} )
2023-12-20 21:57:30 +01:00
log . debug ( ` [FEDI] signed ${ ret . status } => %s ` , ret . data )
return ret . data
2020-01-27 00:47:03 +01:00
} catch ( e ) {
2024-01-10 12:36:19 +01:00
log . error ( "[FEDI] Error in sign and send [%s]: %s" , inbox , e ? . response ? . data ? . error ? ? e ? . response ? . statusMessage ? ? '' + ' ' + String ( e ) )
2020-01-27 00:47:03 +01:00
}
2019-07-31 01:43:08 +02:00
} ,
2019-09-11 12:00:13 +02:00
2019-12-04 00:50:15 +01:00
async sendEvent ( event , type = 'Create' ) {
2019-09-25 14:38:16 +02:00
if ( ! settingsController . settings . enable _federation ) {
2021-04-28 12:44:26 +02:00
log . info ( 'event not send, federation disabled' )
2019-09-13 10:17:44 +02:00
return
}
2019-12-06 00:49:44 +01:00
const followers = await APUser . findAll ( { where : { follower : true } } )
2024-01-08 22:56:14 +01:00
log . debug ( "[FEDI] Sending to %d followers: [%s]" , followers . length , followers . map ( f => f . ap _id ) . join ( ', ' ) )
2019-10-30 15:01:15 +01:00
const recipients = { }
2019-12-04 00:50:15 +01:00
followers . forEach ( follower => {
2023-12-20 21:57:30 +01:00
const sharedInbox = follower ? . object ? . endpoints ? . sharedInbox ? ? follower ? . object ? . inbox
2019-10-28 17:33:20 +01:00
if ( ! recipients [ sharedInbox ] ) { recipients [ sharedInbox ] = [ ] }
2019-09-18 12:55:33 +02:00
recipients [ sharedInbox ] . push ( follower . ap _id )
2019-09-13 10:17:44 +02:00
} )
2019-10-28 17:33:20 +01:00
for ( const sharedInbox in recipients ) {
2021-03-05 14:17:10 +01:00
log . debug ( ` Notify ${ sharedInbox } with event ${ event . title } cc => ${ recipients [ sharedInbox ] . length } ` )
2019-09-11 13:12:05 +02:00
const body = {
2019-09-11 21:20:44 +02:00
id : ` ${ config . baseurl } /federation/m/ ${ event . id } #create ` ,
2019-10-02 21:04:03 +02:00
type ,
2020-11-06 11:05:05 +01:00
to : [ 'https://www.w3.org/ns/activitystreams#Public' ] ,
cc : [ ... recipients [ sharedInbox ] , ` ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } /followers ` ] ,
2019-12-04 00:50:15 +01:00
actor : ` ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } ` ,
2023-03-28 19:02:08 +02:00
object : event . toAP ( settingsController . settings , recipients [ sharedInbox ] )
2019-09-11 13:12:05 +02:00
}
2019-09-26 22:46:04 +02:00
body [ '@context' ] = [
'https://www.w3.org/ns/activitystreams' ,
'https://w3id.org/security/v1' ,
2020-11-06 11:05:05 +01:00
{
2024-01-14 22:26:09 +01:00
toot : 'http://joinmastodon.org/ns#' ,
// A property-value pair, e.g. representing a feature of a product or place. We use this to publish this very same instance
// https://docs.joinmastodon.org/spec/activitypub/#PropertyValue
schema : 'http://schema.org#' ,
ProperyValue : 'schema:PropertyValue' ,
value : 'schema:value' ,
// https://docs.joinmastodon.org/spec/activitypub/#discoverable
"discoverable" : "toot:discoverable" ,
// https://docs.joinmastodon.org/spec/activitypub/#Hashtag
"Hashtag" : "https://www.w3.org/ns/activitystreams#Hashtag" ,
manuallyApprovesFollowers : 'as:manuallyApprovesFollowers' ,
// focal point - https://docs.joinmastodon.org/spec/activitypub/#focalPoint
"focalPoint" : {
"@container" : "@list" ,
"@id" : "toot:focalPoint"
}
2020-11-06 11:05:05 +01:00
} ]
2021-03-18 17:15:50 +01:00
await Helpers . signAndSend ( JSON . stringify ( body ) , sharedInbox )
2019-09-11 12:00:13 +02:00
}
2019-08-02 13:43:28 +02:00
} ,
2024-01-19 00:42:54 +01:00
async parsePlace ( APEvent ) {
const eventController = require ( '../api/controller/event' )
let place
if ( APEvent ? . location ) {
place = {
place _name : APEvent . location ? . name ,
place _address : APEvent . location ? . address ? . streetAddress ? ? APEvent . location ? . address ? . addressLocality ? ? APEvent . location ? . address ? . addressCountry ? ? APEvent . location ? . address ? ? '' ,
place _latitude : APEvent . location ? . latitude ,
place _longitude : APEvent . location ? . longitude
}
}
// could have online locations too
let online _locations = [ ]
if ( APEvent ? . attachment ? . length ) {
online _locations = APEvent . attachment . filter ( a => a ? . type === 'Link' && a ? . href ) . map ( a => a . href )
}
if ( ! place ) {
if ( online _locations ) {
place = { place _name : 'online' }
} else {
throw new Error ( 'No location nor online location' )
}
}
place = await eventController . _findOrCreatePlace ( place )
return [ place , online _locations ]
} ,
async parseAP ( message , actor ) {
const tagController = require ( '../api/controller/tag' )
// has to have an object and a type property..
if ( ! message ? . object || ! message ? . type ) {
log . warn ( '[FEDI] message without `object` or `type` property: %s' , message )
throw new Error ( 'Wrong AP message: no object or type property' )
}
// supporting Announce of a Create
if ( message . type === 'Announce' && message . object ? . type === 'Create' && message . object ? . object ) {
message . object = message . object . object
message . type = 'Create'
}
// we only support Create / Event
if ( message . type === 'Create' && message . object . type === 'Event' ) {
const APEvent = message . object
// validate coming events
const required _fields = [ 'name' , 'startTime' ]
let missing _field = required _fields . find ( required _field => ! APEvent [ required _field ] )
if ( missing _field ) {
log . warn ( ` [FEDI] ${ missing _field } required ` )
throw new Error ( ` ${ missing _field } required ` )
}
// check if this event is new
const ap _id = APEvent . id
const exists = await Event . findOne ( { where : { ap _id } } )
if ( exists ) {
log . warn ( '[FEDI] Avoid creating a duplicated event %s' , ap _id )
return exists
}
const [ place , online _locations ] = await Helpers . parsePlace ( APEvent )
let media = [ ]
const image _url = APEvent ? . attachment ? . find ( a => a ? . mediaType . includes ( 'image' ) && a . url ) ? . url
if ( image _url ) {
const file = await helpers . getImageFromURL ( image _url )
log . debug ( '[FEDI] Download attachment for event %s' , image _url )
media = [ {
url : file . filename ,
height : file . height ,
width : file . width ,
name : APEvent . attachment [ 0 ] ? . name || APEvent . name . trim ( ) || '' ,
size : file . size || 0 ,
focalpoint : APEvent . attachment [ 0 ] ? . focalPoint
} ]
}
// create it
const event = await Event . create ( {
title : APEvent ? . name ? . trim ( ) ? ? '' ,
start _datetime : dayjs ( APEvent . startTime ) . unix ( ) ,
end _datetime : APEvent ? . endTime ? dayjs ( APEvent . endTime ) . unix ( ) : null ,
description : helpers . sanitizeHTML ( APEvent ? . content ? ? APEvent ? . summary ? ? '' ) ,
online _locations ,
media ,
is _visible : true ,
ap _id ,
ap _object : APEvent ,
apUserApId : actor . ap _id ,
} ) . catch ( e => {
console . error ( e )
console . error ( e ? . message )
console . error ( e ? . errors )
return false
} )
await event . setPlace ( place )
// create/assign tags
let tags = [ ]
if ( APEvent . tag ) {
tags = await tagController . _findOrCreate ( APEvent . tag . map ( t => t ? . name . substr ( 1 ) ) )
await event . setTags ( tags )
}
}
} ,
2023-11-21 22:12:21 +01:00
async followActor ( actor ) {
log . debug ( ` Following actor ${ actor . ap _id } ` )
const body = {
'@context' : 'https://www.w3.org/ns/activitystreams' ,
id : ` ${ config . baseurl } /federation/m/ ${ actor . ap _id } #follow ` ,
type : 'Follow' ,
actor : ` ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } ` ,
object : actor . ap _id
}
await Helpers . signAndSend ( JSON . stringify ( body ) , actor . object . endpoints ? . sharedInbox || actor . object . inbox )
await actor . update ( { following : 1 } )
2024-01-19 00:42:54 +01:00
// let's try to get remote outbox
const events = await Helpers . getOutbox ( actor , 10 )
if ( ! events ) { return }
const promises = events . map ( message => Helpers . parseAP ( message , actor ) )
const ret = await Promise . allSettled ( promises )
console . error ( ret )
} ,
async getOutbox ( actor , limit ) {
log . debug ( '[FEDI] Get %s outbox' , actor ? . ap _id )
let events = [ ]
if ( ! actor ? . object ? . outbox ) return
try {
let collection = await Helpers . signAndSend ( '' , actor ? . object ? . outbox , 'get' )
// embedded collection
if ( typeof collection ? . first !== 'string' ) {
return collection . first ? . orderedItems ? ? [ ]
} else if ( /^https?:\/\// . test ( collection ? . first ) ) {
collection = await Helpers . signAndSend ( '' , collection . first , 'get' )
if ( Array . isArray ( collection ? . orderedItems ) ) {
return collection ? . orderedItems ? ? [ ]
}
}
} catch ( e ) {
log . warn ( '[FEDI] getOutbox %s failed: %s' , actor . ap _id , e )
return [ ]
}
2023-11-21 22:12:21 +01:00
} ,
async unfollowActor ( actor ) {
log . debug ( ` Unfollowing actor ${ actor . ap _id } ` )
2023-12-29 18:34:18 +01:00
const object = {
id : ` ${ config . baseurl } /federation/m/ ${ actor . ap _id } #follow ` ,
type : 'Follow' ,
object : actor . ap _id
}
2023-11-21 22:12:21 +01:00
const body = {
'@context' : 'https://www.w3.org/ns/activitystreams' ,
2023-12-29 18:34:18 +01:00
id : ` ${ config . baseurl } /federation/m/ ${ actor . ap _id } #unfollow ` ,
type : 'Undo' ,
2023-11-21 22:12:21 +01:00
actor : ` ${ config . baseurl } /federation/u/ ${ settingsController . settings . instance _name } ` ,
2023-12-29 18:34:18 +01:00
object
2023-11-21 22:12:21 +01:00
}
await Helpers . signAndSend ( JSON . stringify ( body ) , actor . object . endpoints ? . sharedInbox || actor . object . inbox )
return actor . update ( { following : 0 } )
} ,
2023-12-26 13:04:20 +01:00
// get Actor from URL using GET HTTP Signature
2019-10-30 15:01:15 +01:00
async getActor ( URL , instance , force = false ) {
2019-09-12 14:59:51 +02:00
let fedi _user
2019-10-30 15:01:15 +01:00
2019-08-09 01:58:11 +02:00
// try with cache first
2019-10-30 15:01:15 +01:00
if ( ! force ) {
2019-12-04 00:50:15 +01:00
fedi _user = await APUser . findByPk ( URL , { include : Instance } )
2019-10-30 15:01:15 +01:00
if ( fedi _user ) {
2019-11-13 10:56:01 +01:00
return fedi _user
2019-10-30 15:01:15 +01:00
}
}
2019-09-12 14:59:51 +02:00
2023-12-26 13:04:20 +01:00
fedi _user = await Helpers . signAndSend ( '' , URL , 'get' )
2019-10-30 15:01:15 +01:00
2019-09-12 14:59:51 +02:00
if ( fedi _user ) {
2024-01-10 12:36:19 +01:00
log . info ( '[FEDI] Create/Update a new AP User "%s" and associate it to instance "%s"' , URL , instance . domain )
try {
( [ fedi _user ] = await APUser . upsert ( { ap _id : URL , object : fedi _user , instanceDomain : instance . domain , blocked : false } ) )
} catch ( e ) {
log . debug ( '[FEDI] Error in update/create ' )
}
2019-09-12 14:59:51 +02:00
}
return fedi _user
2019-08-02 13:43:28 +02:00
} ,
2023-11-21 22:12:21 +01:00
async getNodeInfo ( instance _url ) {
2023-12-22 09:28:20 +01:00
let nodeInfo = await axios . get ( ` ${ instance _url } /.well-known/nodeinfo ` , { headers : { Accept : 'application/json' } } ) . then ( res => res . data )
2023-12-22 20:58:38 +01:00
if ( nodeInfo ? . links ) {
2023-12-22 09:28:20 +01:00
const supportedVersion = nodeInfo . links . find ( l => l . rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1' || 'http://nodeinfo.diaspora.software/ns/schema/2.0' )
if ( ! supportedVersion ) {
return false
}
const applicationActor = nodeInfo . links . find ( l => l . rel === 'https://www.w3.org/ns/activitystreams#Application' )
nodeInfo = await axios . get ( supportedVersion . href ) . then ( res => res . data )
log . debug ( '[FEDI] getNodeInfo "%s", applicationActor: %s, nodeInfo: %s' , instance _url , applicationActor ? . href , nodeInfo )
return { applicationActor : applicationActor ? . href , nodeInfo }
2023-11-21 22:12:21 +01:00
}
2023-12-22 09:28:20 +01:00
throw new Error ( nodeInfo )
} ,
2023-11-21 22:12:21 +01:00
2019-10-30 15:01:15 +01:00
async getInstance ( actor _url , force = false ) {
2023-12-26 13:04:20 +01:00
log . debug ( ` [FEDI] getInstance for ${ actor _url } ` )
2019-10-30 15:01:15 +01:00
actor _url = new url . URL ( actor _url )
const domain = actor _url . host
const instance _url = ` ${ actor _url . protocol } // ${ actor _url . host } `
let instance
if ( ! force ) {
2019-12-04 00:50:15 +01:00
instance = await Instance . findByPk ( domain )
2023-12-26 21:04:48 +01:00
if ( instance ) {
log . debug ( '[FEDI] Use cached instance: %s' , instance . name )
return instance
}
2019-10-30 15:01:15 +01:00
}
2023-11-21 22:12:21 +01:00
try {
2023-12-26 13:04:20 +01:00
const { applicationActor , nodeInfo } = await Helpers . getNodeInfo ( instance _url )
2024-01-10 12:36:19 +01:00
const [ instance ] = await Instance . upsert ( {
2024-01-08 22:56:14 +01:00
name : nodeInfo ? . metadata ? . nodeName ? ? domain ,
2023-12-26 13:04:20 +01:00
domain ,
data : nodeInfo ,
blocked : false ,
applicationActor
} )
2023-12-26 21:04:48 +01:00
log . debug ( '[FEDI] Create a new instance from %s: %s %s' , instance _url , instance . name , nodeInfo )
2023-12-26 13:04:20 +01:00
return instance
2023-11-21 22:12:21 +01:00
} catch ( e ) {
2023-12-22 09:28:20 +01:00
log . error ( '[FEDI] Wrong nodeInfo returned for "%s": %s' , instance _url , e ? . response ? . data ? ? String ( e ) )
2023-12-26 21:04:48 +01:00
return false
2023-11-21 22:12:21 +01:00
}
2019-10-30 15:01:15 +01:00
} ,
2023-12-26 13:04:20 +01:00
/ * *
* HTTP Signature middleware
* https : //www.w3.org/wiki/SocialCG/ActivityPub/Authentication_Authorization#Signing_requests_using_HTTP_Signatures
* Each POST to / inbox coming from fediverse has to be verified .
* Signature checking needs Actor ' s public key
* /
2019-09-11 19:12:24 +02:00
async verifySignature ( req , res , next ) {
2023-12-26 13:04:20 +01:00
const actor _url = req ? . body ? . actor
// do we have an actor?
if ( ! actor _url ) {
log . warn ( ` [FEDI] Verify Signature: No actor url or empty body ` )
return res . status ( 401 ) . send ( 'Actor not found' )
}
// Get instance's nodeinfo
// getting this from db if it is not the first time we interact with it
const instance = await Helpers . getInstance ( actor _url )
2021-03-05 14:17:10 +01:00
if ( ! instance ) {
2023-12-26 13:04:20 +01:00
log . warn ( ` [FEDI] Verify Signature: Instance not found ${ actor _url } ` )
2021-03-05 14:17:10 +01:00
return res . status ( 401 ) . send ( 'Instance not found' )
}
2023-12-26 13:04:20 +01:00
// Is this instance blocked?
2019-10-30 15:01:15 +01:00
if ( instance . blocked ) {
2023-12-26 13:04:20 +01:00
log . warn ( ` [FEDI] Instance ${ instance . domain } blocked ` )
2019-10-30 15:01:15 +01:00
return res . status ( 401 ) . send ( 'Instance blocked' )
}
2023-12-26 13:04:20 +01:00
// get actor
let ap _actor = await Helpers . getActor ( actor _url , instance )
if ( ! ap _actor ) {
log . info ( ` [FEDI] Actor ${ actor _url } not found ` )
if ( req ? . body ? . type === 'Delete' ) {
2021-12-02 11:39:27 +01:00
return res . sendStatus ( 201 )
}
2021-03-05 14:17:10 +01:00
return res . status ( 401 ) . send ( 'Actor not found' )
}
2023-12-26 13:04:20 +01:00
if ( ap _actor . blocked ) {
log . info ( ` [FEDI] Actor ${ ap _actor . ap _id } blocked ` )
return res . status ( 401 ) . send ( 'Actor blocked' )
}
if ( ! ap _actor ? . object ? . publicKey ? . publicKeyPem ) {
log . info ( ` [FEDI] Actor %s has no publicKey at %s ` , ap _actor . ap _id , actor _url )
return res . status ( 401 ) . send ( 'No public key' )
2019-11-13 10:56:01 +01:00
}
2019-10-30 15:01:15 +01:00
2023-12-26 13:04:20 +01:00
res . locals . fedi _user = ap _actor
2019-08-08 17:48:12 +02:00
2021-04-26 11:25:35 +02:00
// TODO: check Digest // cannot do this with json bodyparser
// const digest = crypto.createHash('sha256')
// .update(req.body)
// .digest('base64')
// if (`SHA-256=${digest}` !== req.headers.signature) {
2021-07-04 00:46:08 +02:00
// log.warn(`Signature mismatch ${req.headers.signature} - ${digest}`)
2021-04-26 11:25:35 +02:00
// return res.status(401).send('Signature mismatch')
// }
2019-09-11 19:12:24 +02:00
// another little hack :/
2019-08-08 17:48:12 +02:00
// https://github.com/joyent/node-http-signature/issues/87
req . url = '/federation' + req . url
2019-08-02 17:29:55 +02:00
const parsed = httpSignature . parseRequest ( req )
2023-12-26 13:04:20 +01:00
if ( httpSignature . verifySignature ( parsed , ap _actor . object . publicKey . publicKeyPem ) ) { return next ( ) }
2019-10-30 15:01:15 +01:00
2019-08-02 17:29:55 +02:00
// signature not valid, try without cache
2023-12-26 13:04:20 +01:00
ap _actor = await Helpers . getActor ( actor _url , instance , true )
if ( ! ap _actor ) {
log . info ( ` [FEDI] Actor ${ actor _url } not found ` )
2021-03-05 14:17:10 +01:00
return res . status ( 401 ) . send ( 'Actor not found' )
}
2023-12-26 13:04:20 +01:00
if ( ! ap _actor ? . object ? . publicKey ? . publicKeyPem ) {
log . info ( ` [FEDI] Actor %s has no publicKey at %s ` , ap _actor . ap _id , actor _url )
return res . status ( 401 ) . send ( 'No public key' )
}
if ( httpSignature . verifySignature ( parsed , ap _actor . object . publicKey . publicKeyPem ) ) {
log . debug ( ` [FEDI] Valid signature from ${ actor _url } ` )
2021-06-25 11:43:50 +02:00
return next ( )
}
2019-10-30 15:01:15 +01:00
2019-08-02 17:29:55 +02:00
// still not valid
2023-12-26 13:04:20 +01:00
log . info ( ` [FEDI] Invalid signature from Actor ${ actor _url } ` )
2019-08-08 17:48:12 +02:00
res . send ( 'Request signature could not be verified' , 401 )
2019-07-30 18:32:26 +02:00
}
}
2019-07-30 18:57:45 +02:00
module . exports = Helpers