Skip to content

devover30/fyers-auth-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fyers-auth-service

A small interactive CLI that walks through the Fyers API v3 OAuth flow and stores the resulting access token in Redis for other services to consume.

Fyers access tokens expire every 24 hours, and the authorisation step requires a human to log in through a browser. This tool exists so that step is a single command run once each morning, rather than something every downstream service has to implement.

What it does

  1. Reads APP_ID, SECRET_KEY, and REDIRECT_URL from the environment
  2. Builds a Fyers generate-authcode URL and prints it to the terminal
  3. You open it, log in, approve access, and get redirected to your redirect URI with a code query parameter
  4. You paste that code back into the terminal
  5. The tool exchanges it at validate-authcode, authenticating with SHA-256(app_id:secret_key) as Fyers requires
  6. It writes access_token and app_id into Redis and exits

Downstream services read those two keys and never handle the OAuth flow themselves. The main consumer is devover30/pivot_service, which reads access_token from Redis on startup and exits with an authentication error if it's missing or expired.

Requirements

  • Rust (2021 edition or later)
  • A running Redis instance on redis://127.0.0.1
  • A Fyers API app with a registered redirect URI

Configuration

Create a .env file in the project root. This file contains secrets — never commit it.

APP_ID=YOUR_FYERS_APP_ID
SECRET_KEY=YOUR_FYERS_SECRET_KEY
REDIRECT_URL=https://your-registered-redirect-uri

All three are required; startup fails if any is missing, and it fails if .env doesn't exist at all.

A .env.example with placeholder values is safe to commit and makes the required shape obvious to anyone cloning the repo.

Running

cargo run

A session looks like this:

Open this URL in a browser, login, and allow access:
https://api-t1.fyers.in/api/v3/generate-authcode?client_id=...&redirect_uri=...&response_type=code&state=stock_buy_alert
Enter the `code` from the redirect URI: <paste code here>

On success the process exits 0 and Redis holds the new token. On failure it logs a warning and exits 1.

What it writes to Redis

Key Contents TTL
access_token Fyers access token none — persists until overwritten
app_id Fyers app ID none — persists until overwritten

Both keys are written to database 0 on redis://127.0.0.1. The Redis URL is currently hardcoded rather than read from the environment.

Related services

This service is the credential half of a small Redis-coupled trading stack. Nothing calls anything else directly — every service reads and writes Redis keys, so each one starts independently.

Service Role Reads Writes
fyers-auth-service (this repo) Interactive OAuth, run once daily access_token, app_id
pivot_service Daily pivot levels from previous-session OHLC access_token pivot:latest:<SYMBOL>

Run order matters: fyers-auth-service must succeed before pivot_service starts, since the latter fails immediately on a missing or expired token. Tokens last 24 hours, so in practice this runs once each morning before the market opens.

Note that pivot_service's README currently attributes token population to a Python helper (fyers_auth_helper.py); this Rust service supersedes it.

Project layout

src/
  main.rs         env loading, orchestration, Redis writes
  api_client.rs   the OAuth flow (generate-authcode → validate-authcode)
  helpers.rs      SHA-256 app_id:secret hashing
  error.rs        ApiError enum with From impls for reqwest/io/url
  models/mod.rs   response and domain structs

models/mod.rs also carries HistoryResp, Candle, StockBuy, and StockBuyDTO, which aren't used by this binary — they're shared/aspirational types kept behind a crate-level #![allow(dead_code)].


Security notes

This section is here because the repository is public. None of the uploaded source files contain a hardcoded credential — every secret comes from the environment, and the SHA-256 hashing of app_id:secret_key matches the Fyers specification. The concerns below are about how secrets are handled at runtime, and about what could end up in the repo by accident.

Before you publish

  • Add .env to .gitignore before the first commit. Nothing in the uploaded files references a .gitignore, so confirm one exists.
  • Check git history, not just the working tree. A secret deleted in a later commit is still readable in an earlier one. git log -p --all -- .env, or a scanner like gitleaks detect / trufflehog, will tell you. If anything turns up, rotating the Fyers secret is the only real fix — rewriting history doesn't help once the repo has been cloned or forked.
  • Rotate any credential that has ever appeared in a commit, a screenshot, a pasted log, or an issue.

Issues in the current code

The access token is printed to stdout. api_client.rs does println!("{:?}", token_resp) after the exchange, which dumps the whole response including the token. Anyone reading the terminal, a CI log, a tee'd output file, or a container log gets a working credential for the next 24 hours. Removing that line, or replacing it with a length-only confirmation, is the highest-value fix here. The same class of bug exists on the consumer side — pivot_service logs the token at INFO on startup — so fixing one without the other still leaves the credential in your logs.

Tokens are stored in Redis without a TTL. SET access_token … with no expiry means the value outlives its own 24-hour validity and sits there until the next run overwrites it. SET_EX with a 24-hour expiry matches the token's real lifetime and bounds the window in which a stale credential is readable. It would also line up with pivot_service, which puts a 24-hour TTL on everything it writes.

Redis has no authentication or transport security. The connection is plain redis://127.0.0.1 — no password, no TLS — and the URL is hardcoded, so it can't be pointed at a secured instance without a recompile. On a single-user machine with Redis bound to loopback that's acceptable; the moment Redis lives on another host or is shared, anyone who can reach the port can read the token. Making the URL configurable (REDIS_URL) and enabling requirepass plus rediss:// for any non-loopback deployment closes that.

The OAuth state parameter is a fixed string and is never verified. state is hardcoded to "stock_buy_alert", and the value returned on the redirect isn't checked against it. state exists to bind the callback to the request that started it; a constant that's visible in a public repo provides no such binding. Generating a random value per run and comparing it on return restores the protection. This matters most if the redirect URI ever becomes a real automated endpoint rather than something you read out of the browser's address bar.

Authorisation codes travel through the browser. The code arrives as a URL query parameter, so it lands in browser history and, if the redirect URI is an http endpoint you don't control, in that server's access logs. Codes are single-use and short-lived, so the exposure is small — but avoid pasting the full redirect URL into chats, issues, or screenshots.

unwrap() on the token. token_resp.access_token.unwrap() panics when Fyers returns an error response, and the panic message is less informative than the message field the API actually sent. Matching on s/code and surfacing message turns a confusing crash into a readable error — and ApiError::AuthenticationFailed, currently defined but never constructed, is the natural variant for it.

Error conversion flattens everything to a string. From<reqwest::Error> calls .to_string(), which can include the request URL. Nothing secret travels in a URL here (the exchange is a POST body), so this is a nuisance rather than a leak — but worth remembering if a query-parameter-based call is added later.

What is fine

  • No credential, key, hash, or token literal appears anywhere in the uploaded source.
  • The secret key is used only to compute the SHA-256 hash; it is never transmitted in plaintext or logged.
  • All API calls go to https:// endpoints.
  • The redirect URI comes from configuration rather than being hardcoded.

Known issues (non-security)

  • models/mod.rs uses a crate-level #![allow(dead_code)], which hides genuine dead code across the whole module. Per-item #[allow] would be more precise.
  • Candle::from_array calls DateTime::from_timestamp(ts, 0).unwrap(), which panics on a timestamp outside the representable range instead of returning None like the rest of the function.
  • error.rs doesn't implement std::error::Error, so ApiError doesn't convert into Box<dyn Error> as ergonomically as it could.
  • There's no refresh-token handling; every run requires the interactive browser login.

Dependencies

  • reqwest — HTTPS calls to the Fyers API
  • redis — async client, multiplexed connection
  • serde / serde_json — request and response bodies
  • sha2 / hex — app ID hashing
  • url — auth URL construction
  • tokio — async runtime
  • tracing / tracing-subscriber — logging
  • dotenvy.env loading
  • chrono — timestamp conversion

About

Generate Auth token from Fyers API

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages