Fix token auth native (#558)

* Fix token auth on Native

* Fix pre-release action
This commit is contained in:
Reckless_Satoshi 2023-05-06 13:42:48 +00:00 committed by GitHub
parent daa1127cfc
commit 25f0b0eea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 135 additions and 146 deletions

View File

@ -15,6 +15,7 @@ on:
jobs:
build-android:
permissions: write-all
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
@ -65,7 +66,7 @@ jobs:
# Create artifacts (only for Release)
# Create app-universal-release APK artifact asset for Release
- name: 'Upload universal .apk Release Artifact (for Release)'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
if: inputs.semver != '' # If this workflow is called from release.yml
with:
name: robosats-${{ inputs.semver }}-universal.apk
@ -73,7 +74,7 @@ jobs:
# Create app-arm64-v8a-release APK artifact asset for Release
- name: 'Upload arm64-v8a .apk Release Artifact (for Release)'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
if: inputs.semver != '' # If this workflow is called from release.yml
with:
name: robosats-${{ inputs.semver }}-arm64-v8a.apk
@ -81,7 +82,7 @@ jobs:
# Create app-armeabi-v7a-release APK artifact asset for Release
- name: 'Upload armeabi-v7a .apk Release Artifact (for Release)'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
if: inputs.semver != '' # If this workflow is called from release.yml
with:
name: robosats-${{ inputs.semver }}-armeabi-v7a.apk
@ -89,7 +90,7 @@ jobs:
# Create app-x86_64-release APK artifact asset for Release
- name: 'Upload x86_64 .apk Release Artifact (for Release)'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
if: inputs.semver != '' # If this workflow is called from release.yml
with:
name: robosats-${{ inputs.semver }}-x86_64.apk
@ -97,7 +98,7 @@ jobs:
# Create app-x86-release APK artifact asset for Release
- name: 'Upload x86 .apk Release Artifact (for Release)'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
if: inputs.semver != '' # If this workflow is called from release.yml
with:
name: robosats-${{ inputs.semver }}-x86.apk
@ -106,14 +107,10 @@ jobs:
- name: 'Create Pre-release'
id: create_release
if: inputs.semver == '' # only if this workflow is not called from a push to tag (a Release)
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ncipollo/release-action@v1.12.0
with:
tag_name: android-${{ steps.commit.outputs.short }}
release_name: robosats-android-${{ steps.commit.outputs.short }}
changelog: mobile/CHANGELOG.md
draft: false
tag: android-${{ steps.commit.outputs.short }}
name: robosats-android-${{ steps.commit.outputs.short }}
prerelease: true
# Upload universal APK to pre-release

View File

@ -49,17 +49,17 @@ jobs:
cd frontend
npm run build
- name: 'Archive Web Basic Build Results'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
with:
name: web-main-js
path: frontend/static/frontend/main.js
- name: 'Archive Web PRO Build Results'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
with:
name: web-pro-js
path: frontend/static/frontend/pro.js
- name: 'Archive Mobile Build Results'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2
with:
name: mobile-web.bundle
path: mobile/html/Web.bundle

View File

@ -67,21 +67,15 @@ jobs:
semver: ${{ needs.check-versions.outputs.semver }}
release:
permissions: write-all
needs: [check-versions, coordinator-image, client-image, android-build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: "Generate Release Changelog"
id: changelog
uses: heinrichreimer/github-changelog-generator-action@v2.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Release
id: create-release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.changelog }}
# Upload app-universal-release APK artifact asset
- name: 'Download universal APK Artifact'

View File

@ -956,7 +956,7 @@ class HistoricalViewSchema:
class StealthViewSchema:
put = {
post = {
"summary": "Update stealth option",
"description": "Update stealth invoice option for the user",
"responses": {

View File

@ -15,7 +15,7 @@ from rest_framework.authentication import (
SessionAuthentication, # DEPRECATE session authentication
)
from rest_framework.authentication import TokenAuthentication
from rest_framework.generics import CreateAPIView, ListAPIView, UpdateAPIView
from rest_framework.generics import CreateAPIView, ListAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
@ -1094,14 +1094,14 @@ class HistoricalView(ListAPIView):
return Response(payload, status.HTTP_200_OK)
class StealthView(UpdateAPIView):
class StealthView(APIView):
authentication_classes = [TokenAuthentication, SessionAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = StealthSerializer
@extend_schema(**StealthViewSchema.put)
def put(self, request):
@extend_schema(**StealthViewSchema.post)
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():

View File

@ -58,13 +58,15 @@ const OrderPage = (): JSX.Element => {
escrow_duration: order.escrow_duration,
bond_size: order.bond_size,
};
apiClient.post(baseUrl, '/api/make/', body, robot.tokenSHA256).then((data: any) => {
if (data.bad_request) {
setBadOrder(data.bad_request);
} else if (data.id) {
navigate('/order/' + data.id);
}
});
apiClient
.post(baseUrl, '/api/make/', body, { tokenSHA256: robot.tokenSHA256 })
.then((data: any) => {
if (data.bad_request) {
setBadOrder(data.bad_request);
} else if (data.id) {
navigate('/order/' + data.id);
}
});
}
};

View File

@ -30,17 +30,13 @@ import { EnableTelegramDialog } from '.';
import BoltIcon from '@mui/icons-material/Bolt';
import SendIcon from '@mui/icons-material/Send';
import NumbersIcon from '@mui/icons-material/Numbers';
import ContentCopy from '@mui/icons-material/ContentCopy';
import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt';
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import { UserNinjaIcon } from '../Icons';
import { systemClient } from '../../services/System';
import { getHost, getWebln } from '../../utils';
import RobotAvatar from '../RobotAvatar';
import { apiClient } from '../../services/api';
import { Robot } from '../../models';
import { Page } from '../../basic/NavBar';
interface Props {
open: boolean;
@ -96,7 +92,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose, robot, setRobot }: Prop
{
invoice: rewardInvoice,
},
robot.tokenSHA256,
{ tokenSHA256: robot.tokenSHA256 },
)
.then((data: any) => {
setBadInvoice(data.bad_invoice ?? '');
@ -110,7 +106,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose, robot, setRobot }: Prop
const setStealthInvoice = (wantsStealth: boolean) => {
apiClient
.put(baseUrl, '/api/stealth/', { wantsStealth }, robot.tokenSHA256)
.post(baseUrl, '/api/stealth/', { wantsStealth }, { tokenSHA256: robot.tokenSHA256 })
.then((data) => setRobot({ ...robot, stealthInvoices: data?.wantsStealth }));
};

View File

@ -249,13 +249,15 @@ const MakerForm = ({
escrow_duration: maker.escrowDuration,
bond_size: maker.bondSize,
};
apiClient.post(baseUrl, '/api/make/', body, robot.tokenSHA256).then((data: object) => {
setBadRequest(data.bad_request);
if (data.id) {
onOrderCreated(data.id);
}
setSubmittingRequest(false);
});
apiClient
.post(baseUrl, '/api/make/', body, { tokenSHA256: robot.tokenSHA256 })
.then((data: object) => {
setBadRequest(data.bad_request);
if (data.id) {
onOrderCreated(data.id);
}
setSubmittingRequest(false);
});
}
setOpenDialogs(false);
};

View File

@ -285,7 +285,7 @@ const TakeButton = ({ order, setOrder, baseUrl, info }: TakeButtonProps): JSX.El
action: 'take',
amount: order.currency == 1000 ? takeAmount / 100000000 : takeAmount,
},
robot.tokenSHA256,
{ tokenSHA256: robot.tokenSHA256 },
)
.then((data) => {
setLoadingTake(false);

View File

@ -76,7 +76,9 @@ const EncryptedTurtleChat: React.FC<Props> = ({
const loadMessages: () => void = () => {
apiClient
.get(baseUrl, `/api/chat/?order_id=${orderId}&offset=${lastIndex}`, robot.tokenSHA256)
.get(baseUrl, `/api/chat/?order_id=${orderId}&offset=${lastIndex}`, {
tokenSHA256: robot.tokenSHA256,
})
.then((results: any) => {
if (results) {
setPeerConnected(results.peer_connected);
@ -175,7 +177,7 @@ const EncryptedTurtleChat: React.FC<Props> = ({
order_id: orderId,
offset: lastIndex,
},
robot.tokenSHA256,
{ tokenSHA256: robot.tokenSHA256 },
)
.then((response) => {
if (response != null) {

View File

@ -173,7 +173,7 @@ const TradeBox = ({
statement,
rating,
},
robot.tokenSHA256,
{ tokenSHA256: robot.tokenSHA256 },
)
.catch(() => {
setOpen(closeAll);

View File

@ -296,7 +296,7 @@ export const useAppStore = () => {
const fetchOrder = function () {
if (currentOrder != undefined) {
apiClient
.get(baseUrl, '/api/order/?order_id=' + currentOrder, robot.tokenSHA256)
.get(baseUrl, '/api/order/?order_id=' + currentOrder, { tokenSHA256: robot.tokenSHA256 })
.then(orderReceived);
}
};
@ -325,9 +325,14 @@ export const useAppStore = () => {
const encPrivKey = newKeys?.encPrivKey ?? robot.encPrivKey ?? '';
const pubKey = newKeys?.pubKey ?? robot.pubKey ?? '';
// On first authenticated request, pubkey and privkey must be in header cookies
systemClient.setCookie('public_key', pubKey.split('\n').join('\\'));
systemClient.setCookie('encrypted_private_key', encPrivKey.split('\n').join('\\'));
// On first authenticated requests, pubkey and privkey are needed in header cookies
const auth = {
tokenSHA256,
keys: {
pubKey: pubKey.split('\n').join('\\'),
encPrivKey: encPrivKey.split('\n').join('\\'),
},
};
if (!isRefresh) {
setRobot((robot) => {
@ -340,7 +345,7 @@ export const useAppStore = () => {
}
apiClient
.get(baseUrl, '/api/robot/', tokenSHA256)
.get(baseUrl, '/api/robot/', auth)
.then((data: any) => {
const newRobot = {
avatarLoaded: isRefresh ? robot.avatarLoaded : false,

View File

@ -2,7 +2,7 @@ class Robot {
constructor(garageRobot?: Robot) {
if (garageRobot != null) {
this.token = garageRobot?.token ?? undefined;
this.tokenSHA256 = garageRobot?.tokenSHA256 ?? undefined;
this.tokenSHA256 = garageRobot?.tokenSHA256 ?? '';
this.pubKey = garageRobot?.pubKey ?? undefined;
this.encPrivKey = garageRobot?.encPrivKey ?? undefined;
}
@ -12,7 +12,7 @@ class Robot {
public token?: string;
public bitsEntropy?: number;
public shannonEntropy?: number;
public tokenSHA256?: string;
public tokenSHA256: string = '';
public pubKey?: string;
public encPrivKey?: string;
public stealthInvoices: boolean = true;

View File

@ -1,32 +1,29 @@
import { ApiClient } from '../api';
import { ApiClient, Auth } from '..';
import { systemClient } from '../../System';
class ApiNativeClient implements ApiClient {
private assetsCache: { [path: string]: string } = {};
private assetsPromises: { [path: string]: Promise<string | undefined> } = {};
private readonly getHeaders: (tokenSHA256?: string) => HeadersInit = (tokenSHA256) => {
private readonly getHeaders: (auth?: Auth) => HeadersInit = (auth) => {
let headers = {
'Content-Type': 'application/json',
};
if (tokenSHA256) {
if (auth) {
headers = {
...headers,
...{
Authorization: `Token ${tokenSHA256}`,
Authorization: `Token ${auth.tokenSHA256}`,
},
};
}
const encrypted_private_key = systemClient.getCookie('encrypted_private_key');
const public_key = systemClient.getCookie('public_key');
if (encrypted_private_key && public_key) {
if (auth?.keys) {
headers = {
...headers,
...{
Cookie: `public_key=${public_key};encrypted_private_key=${encrypted_private_key}`,
Cookie: `public_key=${auth.keys.pubKey};encrypted_private_key=${auth.keys.encPrivKey}`,
},
};
}
@ -52,46 +49,46 @@ class ApiNativeClient implements ApiClient {
return await new Promise((res, _rej) => res({}));
};
public delete: (
baseUrl: string,
path: string,
tokenSHA256?: string,
) => Promise<object | undefined> = async (baseUrl, path, tokenSHA256) => {
return await window.NativeRobosats?.postMessage({
category: 'http',
type: 'delete',
baseUrl,
path,
headers: this.getHeaders(tokenSHA256),
}).then(this.parseResponse);
};
public delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined> =
async (baseUrl, path, auth) => {
return await window.NativeRobosats?.postMessage({
category: 'http',
type: 'delete',
baseUrl,
path,
headers: this.getHeaders(auth),
}).then(this.parseResponse);
};
public post: (
baseUrl: string,
path: string,
body: object,
tokenSHA256?: string,
) => Promise<object | undefined> = async (baseUrl, path, body, tokenSHA256) => {
auth?: Auth,
) => Promise<object | undefined> = async (baseUrl, path, body, auth) => {
return await window.NativeRobosats?.postMessage({
category: 'http',
type: 'post',
baseUrl,
path,
body,
headers: this.getHeaders(tokenSHA256),
headers: this.getHeaders(auth),
}).then(this.parseResponse);
};
public get: (baseUrl: string, path: string, tokenSHA256?: string) => Promise<object | undefined> =
async (baseUrl, path, tokenSHA256) => {
return await window.NativeRobosats?.postMessage({
category: 'http',
type: 'get',
baseUrl,
path,
headers: this.getHeaders(tokenSHA256),
}).then(this.parseResponse);
};
public get: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined> = async (
baseUrl,
path,
auth,
) => {
return await window.NativeRobosats?.postMessage({
category: 'http',
type: 'get',
baseUrl,
path,
headers: this.getHeaders(auth),
}).then(this.parseResponse);
};
public fileImageUrl: (baseUrl: string, path: string) => Promise<string | undefined> = async (
baseUrl,

View File

@ -1,76 +1,75 @@
import { ApiClient } from '..';
import { ApiClient, Auth } from '..';
import { systemClient } from '../../System';
class ApiWebClient implements ApiClient {
private readonly getHeaders: (tokenSHA256?: string) => HeadersInit = (tokenSHA256) => {
private readonly getHeaders: (auth?: Auth) => HeadersInit = (auth) => {
let headers = {
'Content-Type': 'application/json',
};
if (tokenSHA256) {
if (auth) {
headers = {
...headers,
...{
Authorization: `Token ${tokenSHA256}`,
Authorization: `Token ${auth.tokenSHA256}`,
},
};
}
// set cookies before sending the request
if (auth?.keys) {
systemClient.setCookie('public_key', auth.keys.pubKey);
systemClient.setCookie('encrypted_private_key', auth.keys.encPrivKey);
}
return headers;
};
public post: (
baseUrl: string,
path: string,
body: object,
tokenSHA256?: string,
) => Promise<object> = async (baseUrl, path, body, tokenSHA256) => {
const requestOptions = {
method: 'POST',
headers: this.getHeaders(tokenSHA256),
body: JSON.stringify(body),
public post: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object> =
async (baseUrl, path, body, auth) => {
const requestOptions = {
method: 'POST',
headers: this.getHeaders(auth),
body: JSON.stringify(body),
};
return await fetch(baseUrl + path, requestOptions).then(
async (response) => await response.json(),
);
};
return await fetch(baseUrl + path, requestOptions).then(
async (response) => await response.json(),
);
};
public put: (
baseUrl: string,
path: string,
body: object,
tokenSHA256?: string,
) => Promise<object> = async (baseUrl, path, body, tokenSHA256) => {
const requestOptions = {
method: 'PUT',
headers: this.getHeaders(tokenSHA256),
body: JSON.stringify(body),
public put: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object> =
async (baseUrl, path, body, auth) => {
const requestOptions = {
method: 'PUT',
headers: this.getHeaders(auth),
body: JSON.stringify(body),
};
return await fetch(baseUrl + path, requestOptions).then(
async (response) => await response.json(),
);
};
return await fetch(baseUrl + path, requestOptions).then(
async (response) => await response.json(),
);
};
public delete: (baseUrl: string, path: string, tokenSHA256?: string) => Promise<object> = async (
public delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object> = async (
baseUrl,
path,
tokenSHA256,
auth,
) => {
const requestOptions = {
method: 'DELETE',
headers: this.getHeaders(tokenSHA256),
headers: this.getHeaders(auth),
};
return await fetch(baseUrl + path, requestOptions).then(
async (response) => await response.json(),
);
};
public get: (baseUrl: string, path: string, tokenSHA256?: string) => Promise<object> = async (
public get: (baseUrl: string, path: string, auth?: Auth) => Promise<object> = async (
baseUrl,
path,
tokenSHA256,
auth,
) => {
return await fetch(baseUrl + path, { headers: this.getHeaders(tokenSHA256) }).then(
return await fetch(baseUrl + path, { headers: this.getHeaders(auth) }).then(
async (response) => await response.json(),
);
};

View File

@ -1,21 +1,16 @@
import ApiWebClient from './ApiWebClient';
import ApiNativeClient from './ApiNativeClient';
export interface Auth {
tokenSHA256: string;
keys?: { pubKey: string; encPrivKey: string };
}
export interface ApiClient {
post: (
baseUrl: string,
path: string,
body: object,
tokenSHA256?: string,
) => Promise<object | undefined>;
put: (
baseUrl: string,
path: string,
body: object,
tokenSHA256?: string,
) => Promise<object | undefined>;
get: (baseUrl: string, path: string, tokenSHA256?: string) => Promise<object | undefined>;
delete: (baseUrl: string, path: string, tokenSHA256?: string) => Promise<object | undefined>;
post: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>;
put: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>;
get: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>;
delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>;
fileImageUrl?: (baseUrl: string, path: string) => Promise<string | undefined>;
}