<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: talha Atif</title>
    <description>The latest articles on DEV Community by talha Atif (@talha_atif_22).</description>
    <link>https://dev.to/talha_atif_22</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2086494%2Fa8ece539-195f-4d49-bfd6-f2a722ed95ac.png</url>
      <title>DEV Community: talha Atif</title>
      <link>https://dev.to/talha_atif_22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/talha_atif_22"/>
    <language>en</language>
    <item>
      <title>OAuth Is Not "Login." I Found That Out the Hard Way in Production</title>
      <dc:creator>talha Atif</dc:creator>
      <pubDate>Thu, 01 Oct 2026 14:33:43 +0000</pubDate>
      <link>https://dev.to/talha_atif_22/oauth-is-not-login-i-found-that-out-the-hard-way-in-production-2dp2</link>
      <guid>https://dev.to/talha_atif_22/oauth-is-not-login-i-found-that-out-the-hard-way-in-production-2dp2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x5ew7fat3e5riwntt9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x5ew7fat3e5riwntt9f.png" alt="Cover image for the article OAuth Is Not Login, showing a dark themed diagram of the OAuth flow from Your App to OAuth Provider to Scoped Token, with tags for OAuth 1.0 to 2.0, PKCE for SPAs and Mobile, and JWT vs OAuth Token" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most people, including a lot of engineers, think OAuth means "the button that says Sign in with Google." That's not wrong exactly, but it's like saying a car is "the thing with a steering wheel." Technically true, misses basically everything that matters.&lt;/p&gt;

&lt;p&gt;I want to actually walk through what OAuth is, why it exists, what it costs you, and the specific way it bit me in production, because the bug taught me more about OAuth than any tutorial did.&lt;/p&gt;

&lt;p&gt;If I use a term that sounds like engineer-speak, I'll flag it like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert:&lt;/strong&gt; here's the jargon, here's what it actually means in plain English.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's go.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OAuth actually is
&lt;/h2&gt;

&lt;p&gt;OAuth is not a login system. OAuth is a permission system. It answers one question: "can this app do this specific thing on my behalf, without me handing it my password?"&lt;/p&gt;

&lt;p&gt;That's it. That's the whole idea.&lt;/p&gt;

&lt;p&gt;Before OAuth, if you wanted some third party app to, say, post to your Twitter or read your Gmail contacts, you had exactly one option: give that app your actual Twitter or Gmail password. The app would log in as you, directly, using your real credentials. Which means that random app now has full access to your account forever, until you go change your password, and even then you're trusting a third party company stored your password responsibly in the first place.&lt;/p&gt;

&lt;p&gt;OAuth replaces that with a system where you never hand your password to the third party app at all. Instead, you briefly go to Google (or whoever), log in there, and Google hands the app a token, a kind of limited, revocable permission slip, instead of your actual credentials.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: token.&lt;/strong&gt; Think of it as a hotel key card instead of your house key. It opens specific doors, for a specific amount of time, and the hotel can deactivate it instantly without you having to change your actual house locks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Could the internet work without OAuth
&lt;/h2&gt;

&lt;p&gt;Technically, yes. Plenty of early 2000s apps worked exactly how I described above, you just gave them your password. It's not that the internet collapses without OAuth.&lt;/p&gt;

&lt;p&gt;What actually happens without it is worse in a slower, quieter way. Every app you connect to anything ends up holding a copy of your real password. A breach at any one of those apps means your actual account password leaks, not just some limited token. You can't selectively revoke access ("let this app see my calendar but not send emails as me") because the app either has your full password or it has nothing. And you can't tell your bank, your email provider, or your social accounts "trust these five apps, not that one," because from their point of view every connected app looks identical: someone logged in with your password.&lt;/p&gt;

&lt;p&gt;So yes, the world could work without OAuth. It would just be a world where a breach anywhere is a breach everywhere, and "revoke access" isn't really a button that exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick word on OAuth 1.0, the version before this one
&lt;/h2&gt;

&lt;p&gt;Before the OAuth everyone actually uses today, there was OAuth 1.0, back around 2007. Same basic idea, token instead of password, but the mechanics were painful. Every single request had to be cryptographically signed by the app using a shared secret, a process involving HMAC-SHA1 and exact timestamps, before the server would trust it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: cryptographic.&lt;/strong&gt; Just means "based on math that's specifically designed to be really, really hard to fake or reverse without the right secret ingredient." When something is described as cryptographically secure, breaking it isn't impossible, it's just so expensive and slow that nobody sane bothers trying.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: signature (signing).&lt;/strong&gt; Not a handwritten signature. A short code generated from the actual contents of a message plus a secret value, proving two things at once: this message wasn't altered in transit, and it genuinely came from whoever holds that secret. Change one character of the message and the signature stops matching, which is exactly how tampering gets caught.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: secret.&lt;/strong&gt; A private value, basically a password, known only to the two parties who are supposed to trust each other. Whoever holds the secret can prove they are who they claim to be. Whoever doesn't, can't fake it convincingly. The whole system falls apart the moment that "secret" stops actually being secret.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: HMAC-SHA1 signing.&lt;/strong&gt; A way of mathematically stamping each request so the server can verify nothing was tampered with in transit, instead of just trusting an encrypted connection to do that job. You don't need the math, just know it meant extra, fiddly cryptography homework on every single request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice this meant developers spent real time fighting signature mismatches and clock sync issues instead of building their actual app. It worked, but it was miserable, and it still assumed every app had somewhere safe to keep a permanent secret. OAuth 2.0 showed up in 2012 and basically said, let's just lean on HTTPS to protect the data in transit like everyone already does for everything else, and drop the per-request signing entirely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: HTTPS.&lt;/strong&gt; The padlock in your browser's address bar. It encrypts everything traveling between your browser and the server, so anyone snooping on the connection, a nosy coffee shop WiFi for instance, sees scrambled nonsense instead of your actual data. OAuth 2.0 leans on this instead of signing every request itself, which is most of why it's so much simpler than OAuth 1.0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simpler for developers, which is exactly why OAuth 2.0 is the one that actually won and is what the rest of this article has been describing the whole time.&lt;/p&gt;

&lt;p&gt;But that "every app has somewhere safe to keep a secret" assumption didn't go away, it just got quietly inherited by OAuth 2.0. And a few years later, that assumption stopped being true for a huge chunk of apps. More on that in a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it actually works, the short version
&lt;/h2&gt;

&lt;p&gt;Here's the flow in plain English, using "Sign in with Google" as the example:&lt;/p&gt;

&lt;p&gt;You click the button. The app sends you over to Google, not the other way around, you never type your Google password into the third party app's own form. At Google, you log in (or you're already logged in) and Google shows you a screen saying "This app wants to see your email and profile picture, allow it?" You say yes. Google then redirects you back to the app with a short-lived code. The app takes that code and, behind the scenes, trades it with Google for an actual token. From then on, the app uses that token to ask Google for the specific things you approved, nothing more.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: scope.&lt;/strong&gt; A scope is the specific permission being requested, like "read your email address" or "post on your behalf." Apps are supposed to ask for the narrowest scope that does the job, though in practice a lot of apps ask for way more than they need, because broader scopes are easier to code against and nobody's really checking.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: redirect URI.&lt;/strong&gt; This is the exact web address Google is allowed to send you back to after login. It's locked in advance when the app registers with Google, specifically so an attacker can't trick Google into sending your freshly issued code to some address they control instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That whole flow above quietly assumes one more thing: that the app trading the code for a token is a proper backend server, one that can hold onto a permanent, secret password of its own (a "client secret") to prove to Google "yes, this request really is from the real app." Which brings us to a problem nobody really had to think about back in 2012.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem nobody saw coming, apps that can't keep a secret
&lt;/h2&gt;

&lt;p&gt;Fast forward a few years and suddenly half the apps in the world are single-page apps running entirely in the browser, or mobile apps sitting on someone's phone. A SPA's entire codebase gets shipped straight to the user, anyone can pop open dev tools and read it line by line. A mobile app's installer can be decompiled by anyone with a weekend and mild curiosity. There is nowhere to actually hide a secret in either case. Whatever you bake in, someone can pull back out, which is a uniquely bad punchline for something literally called a "client secret."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: client secret.&lt;/strong&gt; A password-like value, known only to the real backend and Google, used to prove a token request is genuinely coming from the real app and not an imposter. Fine when there's a server to keep it locked up. Useless the moment the "app" is just JavaScript running on a stranger's laptop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fix is called PKCE, Proof Key for Code Exchange, and it's pronounced "pixie," which might be the single most delightful name in all of security engineering.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: PKCE.&lt;/strong&gt; Instead of a long-lived secret baked into the app, the app invents a random, one-time value right before login starts, the code verifier, and sends over a hashed version of it, the code challenge, when kicking off the flow. At the very end, when trading the code for a token, it has to produce that original, un-hashed value again. If someone intercepts the authorization code mid-flow, it's useless to them, they don't have the random value that generated it, and they can't fake having it either.&lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: hashing.&lt;/strong&gt; A one-way math function that turns any input into a fixed-length scrambled output, and crucially, you can't run it backwards to get the original input out again. Same input always produces the same output, which is exactly what makes it useful here, the app can prove "I know the original value" without ever saying the original value out loud until the very last step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No permanent secret to leak, because there was never a permanent secret to begin with, just a fresh, disposable value that self-destructs after one use. Genuinely a cleaner idea than the thing it replaced, and these days it's recommended for basically every app, server backed or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade-off nobody puts on the slide
&lt;/h2&gt;

&lt;p&gt;Here's what the "just add OAuth" tutorials gloss over: you're trading simplicity for control, and that trade costs real engineering effort.&lt;/p&gt;

&lt;p&gt;With a plain username and password system, you own the entire flow. One table, one hashing function, done. With OAuth, you now depend on a third party's uptime, their API changing underneath you, tokens that expire and need refreshing, and an entire second system, your own backend, that has to track who's logged in even though the actual login happened somewhere else entirely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: refresh token.&lt;/strong&gt; Access tokens are deliberately short-lived, often an hour or less, so if one leaks, the damage window is small. A refresh token is a longer-lived, more sensitive token your backend holds onto quietly, so it can go get a fresh access token without making the user log in again every hour. Lose control of a refresh token and you've basically lost control of the session, so these need to be stored far more carefully than access tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the actual trade-off. You get: no passwords for you to store or leak, users can revoke access with one click from Google's side, scoped and limited permissions, and single sign-on convenience. You pay for it with: more moving parts, a dependency on an external provider, token lifecycle management, and a genuinely larger surface area for things to quietly go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I actually use this in production
&lt;/h2&gt;

&lt;p&gt;On one platform I worked on, we had Google OAuth as a social login option sitting alongside our own email and password system, with our own JWT issued after either path succeeded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: JWT.&lt;/strong&gt; Short for JSON Web Token. This is different from an OAuth access token, even though people use the words interchangeably all the time. A JWT is a signed, self-contained token your own backend creates, that your own backend can verify without a database lookup, basically a sealed, tamper-proof note saying "this is user 482, and I, the server, vouch for that." OAuth tokens come from Google. JWTs, in this setup, come from us.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction, two different kinds of token doing two different jobs, is exactly where things went wrong for me once.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually goes wrong: my real production bug
&lt;/h2&gt;

&lt;p&gt;We had a production authentication bug where users were randomly getting logged out, or logged in as the wrong state, with no clear pattern. Intermittent, hard to reproduce locally, the worst kind of bug.&lt;/p&gt;

&lt;p&gt;The root cause, once I actually traced it through production logs, was almost embarrassingly simple: the OAuth token coming back from the social login flow and our own JWT were both being stored under the same cookie key. Whichever one got written last silently overwrote the other. So depending on timing, a user's session cookie might end up holding Google's OAuth token instead of our own JWT, or vice versa, and the backend would try to verify it as the wrong type of token entirely, fail, and drop the session.&lt;/p&gt;

&lt;p&gt;Nobody designed it that way on purpose. It happened because OAuth tokens and JWTs look similar enough, both are just strings you shove in a cookie, that it's easy to treat them as interchangeable "auth stuff" instead of two distinct tokens with two distinct lifecycles and two distinct purposes. The fix was straightforward once I actually understood the distinction: give each token its own, clearly named cookie key, so they could never collide again.&lt;/p&gt;

&lt;p&gt;The real lesson wasn't the one line fix. It was that OAuth tokens and your own session tokens are not the same thing wearing a different hat, and treating them as the same thing is exactly the kind of assumption that only breaks in production, under real timing and real concurrent requests, never in your local dev environment where everything happens conveniently one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other pitfalls worth knowing before you hit them
&lt;/h2&gt;

&lt;p&gt;A few more ways this goes sideways, things I've seen or would actively watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storing tokens in localStorage&lt;/strong&gt; instead of an httpOnly cookie, which means any cross-site scripting vulnerability on your site can just read the token straight out and walk away with the user's session.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;Jaggy jaggy alert: httpOnly cookie.&lt;/strong&gt; A cookie flagged so that JavaScript running in the browser can't read it at all, only the browser itself can send it along with requests. It's a deliberate wall against exactly the kind of theft I just described.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mismatched redirect URIs&lt;/strong&gt; between what's registered and what the app actually sends, which just breaks login outright and is a surprisingly common first deploy bug when you move from a local dev URL to a real production domain and forget to register the new one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Asking for way more scopes than you need&lt;/strong&gt;, which makes users nervous at the consent screen and gives you a bigger blast radius if your token ever does leak.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treating "the user has an OAuth token" as "the user is logged in"&lt;/strong&gt; without actually verifying who they are. OAuth by itself is about authorization, what you're allowed to do, not identity. Actual login on top of OAuth is a separate layer called OpenID Connect, and conflating the two is a genuinely common source of security bugs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So, do you actually need it
&lt;/h2&gt;

&lt;p&gt;If you're building something small and internal, OAuth is probably overkill, a simple username and password system with properly hashed passwords is less to maintain and less to get wrong. OAuth earns its complexity the moment you want users to log in with an identity they already have elsewhere, or you want to act on a third party service on their behalf without ever touching their password. At that point, yes, deal with the token juggling, because the alternative, a world where "give the app your password" is normal, is worse in ways that only show up after something's already gone wrong.&lt;/p&gt;

&lt;p&gt;That's the real shape of it. Not a login button. A permission system, with real trade-offs, that will absolutely find the one gap in your assumptions if you let two different tokens share a cookie key and call it a day.&lt;/p&gt;

</description>
      <category>oauth</category>
      <category>ai</category>
      <category>backend</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Looking for feedback on my spring boot project</title>
      <dc:creator>talha Atif</dc:creator>
      <pubDate>Sun, 06 Apr 2025 14:33:25 +0000</pubDate>
      <link>https://dev.to/talha_atif_22/looking-for-feedback-on-my-spring-boot-project-1ooj</link>
      <guid>https://dev.to/talha_atif_22/looking-for-feedback-on-my-spring-boot-project-1ooj</guid>
      <description>&lt;p&gt;I’ve been working on a personal full-stack project where users can browse and book event tickets via a native Android app backed by a Spring Boot API. I’d love your honest feedback, whether you’re into backend stuff, Android, or full-stack work!&lt;/p&gt;

&lt;p&gt;🔧 &lt;strong&gt;Spring Boot Backend Highlights:&lt;/strong&gt;&lt;br&gt;
🛠️ MongoDB used for storing events, users, bookings, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔐 JWT-based authentication for secure login/registration&lt;/li&gt;
&lt;li&gt;💳 Stripe integration for payments&lt;/li&gt;
&lt;li&gt;📊 Swagger UI docs included for quick API testing&lt;/li&gt;
&lt;li&gt;🚫 Added rate limiting to avoid abuse&lt;/li&gt;
&lt;li&gt;☁️ Deployed on Heroku (for quick demos &amp;amp; testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Backend GitHub Repo:&lt;/strong&gt; [&lt;a href="https://github.com/Progambler227788/TicketManagement-SpringBoot" rel="noopener noreferrer"&gt;https://github.com/Progambler227788/TicketManagement-SpringBoot&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;📱 &lt;strong&gt;Android App Features (Kotlin):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎟️ Book event tickets directly from the app&lt;/li&gt;
&lt;li&gt;🌟 View trending and featured events&lt;/li&gt;
&lt;li&gt;💼 Pay using Wallet or Stripe&lt;/li&gt;
&lt;li&gt;👤 Update your profile&lt;/li&gt;
&lt;li&gt;🔍 Filter events by category&lt;/li&gt;
&lt;li&gt;💡 Smooth UI with loading animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Android GitHub Repo:&lt;/strong&gt; [&lt;a href="https://github.com/Progambler227788/TickoJet-Android" rel="noopener noreferrer"&gt;https://github.com/Progambler227788/TickoJet-Android&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;🚀 Planned Features (Coming Soon):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploading images via AWS S3&lt;/li&gt;
&lt;li&gt;In-app ticket cancellation (backend is ready!)&lt;/li&gt;
&lt;li&gt;Adding a referral program with rewards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 Note: The data used in the app is currently dummy/sample data for demonstration purposes.&lt;/p&gt;

&lt;p&gt;🧪 &lt;strong&gt;Feedback I’d Love:&lt;/strong&gt;&lt;br&gt;
Any code quality or design pattern improvements?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suggestions on UI/UX?&lt;/li&gt;
&lt;li&gt;Ideas for new features?&lt;/li&gt;
&lt;li&gt;Is this production ready, or what should I improve before that?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading, I’m open to all suggestions and genuinely want to grow as a developer 🙌&lt;/em&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>kotlin</category>
      <category>java</category>
      <category>android</category>
    </item>
  </channel>
</rss>
