How to Write Upwork Proposals That Stand Out From 50+ Other Applicants

Upwork proposals freelance writing tools ai content generator business automation software automated writing
Nikita Shekhawat
Nikita Shekhawat

Junior SEO Specialist

 
January 7, 2026 9 min read
How to Write Upwork Proposals That Stand Out From 50+ Other Applicants

TL;DR

This guide covers how to beat the 50+ applicant crowd on Upwork by using smart hooks and proof of work. You will learn to spot high-value clients and use automated tools for faster, better responses that actually get you hired.

The Role of Refresh Tokens in Modern Auth

Ever wonder why you can stay logged into your mobile banking app for weeks but your work email kicks you out every hour? It’s all about the balancing act between keeping things secure and not making users want to rip their hair out.

In a modern auth setup, we usually use json web tokens (jwts) as access tokens. They’re great because they are stateless, but they have a big problem: if someone steals one, they own that session until it expires. To mitigate this risk, we make them short-lived—sometimes just 5 or 15 minutes. (Why JWTs Valid After Logout: Pentester's Guide to Tokens)

But if we only had access tokens, your users would have to type their password every 15 minutes. That’s a nightmare for a retail app or a healthcare portal where doctors need fast access to patient records.

  • Security vs. UX: Short access tokens limit the "blast radius" of a leak, but they create massive friction.
  • The Refresh Token Solution: This is a long-lived credential used solely to get a new access token without the user doing anything.
  • Silent Re-auth: It happens in the background. Your app hits the /token endpoint, swaps the refresh token for a new access token, and the user keeps scrolling.

Diagram 1

I've seen plenty of teams get these mixed up, but they serve totally different masters. According to Auth0 by Okta, refresh tokens are essentially a way to maintain a session without keeping the user's actual credentials around in the browser or app memory.

  • Payloads: Access tokens carry "scopes" (what the user can do, like read:reports). Refresh tokens usually don't carry permissions; they are just a key to prove you're still "you."
  • Storage: You’d usually keep an access token in memory (volatile). A refresh token might live in a HttpOnly cookie or secure storage on a phone so it survives a page reload or app restart.
  • Visibility: Your Resource api (the one with the data) sees the access token every time. However, that Resource api should remain totally agnostic of refresh tokens—it never even sees them. Only the Auth api or identity provider handles the refresh exchange.

In a finance app, for example, the access token lets you view your balance. If that token expires while you're looking at your spending habits, the refresh token grabs a new one silently. But if you try to transfer $10k, the system might ignore the token and ask for a fresh login or mfa anyway.

Now that we know why we need 'em, let's look at how the actual handshake works under the hood.

Implementation Guide for Enterprise CIAM

Setting up an enterprise-grade identity flow is usually where things get messy, especially when you realize your "standard" oauth setup doesn't handle disconnected users or long-running background tasks very well. If you’ve ever had a retail app crash because a token expired while a user was mid-checkout, you know exactly why we need to get the implementation right the first time.

To even get a refresh token, you gotta ask for it explicitly during the initial auth request. In most oidc providers, this means adding the offline_access scope. Note: You also have to make sure your client registration on the Identity Provider (idp) is configured to permit the "Refresh Token" grant type. If you don't check that box on the server side, the idp will just ignore your scope request and you won't get a token back.

  • Scope Injection: Ensure your frontend or backend client includes offline_access in the authorization code request.
  • User Consent: In B2C scenarios, like a healthcare portal, the user might see a prompt asking for permission to "access data offline"—this is just the system asking if it's okay to stay logged in.
  • Backend Exchange: Once you get the auth code, your backend swaps it at the /token endpoint. This is where you get the goods: the access token and the refresh token.

I've found that using a provider like SSOJet simplifies this because they handle the heavy lifting of enterprise-grade token management, especially when you're dealing with complex B2B SaaS requirements where different tenants might have different session policies.

Diagram 2

The trickiest part isn't getting the token; it's using it without breaking the user experience. You don't want to refresh the token on every single api call—that’s just extra latency—but you also can't wait for the app to fail.

A common pattern is to catch a 401 Unauthorized error in your interceptor. When that happens, you pause all outgoing requests, hit the refresh endpoint, and then retry the original request with the new token.

If your dashboard fires off five api calls at once and the token is expired, all five will trigger a refresh. This is a "thundering herd" problem. You need to implement atomic refresh calls using a queue or subscriber pattern.

// Handling the thundering herd with a refresh promise
let isRefreshing = false;
let refreshSubscribers = [];

function subscribeTokenRefresh(cb) { refreshSubscribers.push(cb); }

function onRerefreshFetched(token) { refreshSubscribers.map(cb => cb(token)); refreshSubscribers = []; }

async function handleTokenRefresh(failedRequest) { if (!isRefreshing) { isRefreshing = true; try { // The Auth/BFF API handles the exchange, not the Resource API const { newAccessToken } = await api.post('/auth/refresh'); isRefreshing = false; onRerefreshFetched(newAccessToken); } catch (err) { isRefreshing = false; redirectToLogin(); return Promise.reject(err); } }

// If a refresh is already happening, we wait for it return new Promise((resolve) => { subscribeTokenRefresh((token) => { failedRequest.headers['Authorization'] = 'Bearer ' + token; resolve(axios(failedRequest)); }); }); }

According to Okta, implementing token rotation—where you get a new refresh token every time you use an old one—is the gold standard for security, but it makes this race condition logic even more critical.

Next, we'll dive into the scary stuff: what happens when these tokens get stolen and how to lock down your storage.

Security Best Practices to Prevent a breach

So, you’ve got refresh tokens working. Great. But now you’ve basically handed out a "skeleton key" that stays valid for days or weeks. If that key gets swiped, the attacker has a permanent backstage pass to your user's data.

The smartest way to handle this is Refresh Token Rotation. Instead of one token that lasts forever, the auth server gives you a brand new refresh token every single time you use the old one to get a new access token.

  • One-time use: As soon as the client uses RT_1 to get AT_2, the server marks RT_1 as dead and issues RT_2.
  • Chain of Trust: This creates a continuous chain. If an attacker snags a token but the legitimate user uses it first, the attacker's copy is already useless.
  • Handling Flaky Networks: This is the part that usually breaks. If a mobile app on a train loses signal right after sending the refresh request, it might never see the new token. You have to allow a small "grace period" (like 30 seconds) where the old token still works, or you'll log out half your users every time they go through a tunnel.

Diagram 3

This is where things get really cool—and a bit aggressive. If your server sees a refresh token that has already been used (and rotated), it shouldn't just say "access denied." It should panic.

According to Microsoft, a 2023 report on identity security suggests that token theft is a leading cause in session hijacking. (Session Hijacking 2.0 — The Latest Way That Attackers are ...) When a used token shows up again, it’s a massive red flag that two different "people" have the same token.

  • The Kill Switch: If RT_1 is used a second time, the server knows a breach happened. It should immediately revoke the entire token family.
  • User Impact: Yes, this logs out the real user too. But it’s better to have a frustrated user who has to log back in with mfa than a compromised account.
  • Alerting: You need to log this. If you see a spike in reuse detections in a specific region, you’re likely under a coordinated session-stuffing attack.

"Reuse detection is the only way to stop an attacker who has successfully exfiltrated a long-lived token from a browser's local storage."

Here is a quick look at how you might check for reuse in a node.js/redis backend:

async function refreshMyToken(oldTokenId) {
  const tokenStatus = await redis.get(`token:${oldTokenId}`);
  

if (tokenStatus === 'used') { // UH OH. Someone is trying to reuse a token. // Revoke everything for this user id. await revokeAllSessions(userId); throw new Error("Security breach detected. Sessions invalidated."); }

// proceed with normal rotation... }

This kind of proactive defense is what separates a basic login page from a real enterprise identity system. It’s a bit more work to code, but it saves you from a nightmare pr disaster later.

Next, let's talk about how to actually clear these out when a user hits "logout" so you don't leave any "ghost" sessions hanging around.

Storage and Revocation Strategies

Logout is more than just deleting a cookie on the client side. If you don't tell the auth server that the token is dead, an attacker who already stole it can still use it. This is why you need a revocation endpoint.

When a user clicks "logout" in a retail app or a b2b saas dashboard, your backend should hit the identity provider's /revoke endpoint. This invalidates the refresh token immediately.

For web apps, we really shouldn't be putting these in localStorage. Any rogue script or malicious npm package can just scrape it. Instead, use HttpOnly cookies with the Secure and SameSite=Strict flags because javascript can't touch them.

If you want to be even more secure, use the BFF (Backend for Frontend) pattern. In this setup, the browser doesn't even hold the tokens. Instead, a small middle-layer server stores the tokens in a secure server-side session. The browser just gets a session cookie. When the session needs a new access token, the BFF handles the refresh logic server-to-server, keeping the refresh token entirely out of the browser's reach.

  • Web apps: Use HttpOnly cookies or the BFF pattern.
  • Mobile apps: Always use the native secure storage like iOS Keychain or Android Keystore. Don't just dump tokens into a plain json file or shared preferences.
  • Backend: You need a database or a fast cache like redis to track which refresh tokens are still valid.

Diagram 4

I've seen teams struggle with "Global Logout," especially when a user has five different devices. The cleanest way is to maintain a token family or a session id. When one device triggers a logout or a security alert, you can wipe every token associated with that session id in one go.

According to IETF RFC 7009, which is the standard for oauth 2.0 token revocation, the authorization server must provide a way for clients to signal that a token is no longer needed. This is crucial for preventing "ghost sessions" where a user thinks they're logged out, but their api access is still active.

The reality is that identity is never "set it and forget it." You have to balance making it easy for your users to stay logged in while keeping the bad guys out. Using rotation, secure storage, and a solid revocation strategy is how you build a system that people actually trust.

Honestly, it's about being proactive. Don't wait for a breach to realize your tokens are sitting in plain text or that your logout button doesn't actually do anything on the server. Get these basics right, and you're already ahead of 90% of the apps out there.

Nikita Shekhawat
Nikita Shekhawat

Junior SEO Specialist

 

Nikita Shekhawat is a junior SEO specialist focused on off-page SEO and link-building initiatives that support organic visibility. Her work involves managing outreach, guest collaborations, and contextual link placements across technology-focused domains. She takes a practical, execution-oriented approach to improving authority and discoverability through consistent, relationship-driven SEO efforts.

Related Articles

AI Video Generators for Social Media Ads: The 2026 Guide to Faster, Cheaper, High-Converting Content
AI video generators

AI Video Generators for Social Media Ads: The 2026 Guide to Faster, Cheaper, High-Converting Content

Discover the best AI video generators for social media ads in 2026. Create high-converting videos faster, cheaper, and without editing skills.

By Ankit Agarwal January 1, 2026 3 min read
Read full article
How to Create Client-Ready Content Briefs in 10 Minutes
content briefs

How to Create Client-Ready Content Briefs in 10 Minutes

Learn how to build client-ready content briefs in just 10 minutes using ai writing tools and smart SEO research. Stop revision cycles today.

By Mohit Singh January 5, 2026 4 min read
Read full article
Customer Email Templates Every Small Business Needs
Customer Email Templates Every Small Business Needs

Customer Email Templates Every Small Business Needs

Discover the essential customer email templates every small business needs to grow. From welcome emails to follow-ups, learn how to automate communication with ai tools.

By Ankit Agarwal January 2, 2026 6 min read
Read full article
The Student's Guide to Writing Research Paper Abstracts That Get Read
AI writing assistant

The Student's Guide to Writing Research Paper Abstracts That Get Read

Learn how to write research paper abstracts that get read. Discover tips on structure, clarity, and using ai writing tools to improve your academic work.

By Hitesh Kumar Suthar December 31, 2025 6 min read
Read full article