'Angular and Flask Authorization workflow and getting token silently

I'm new to web development, and am working on building a few sites to learn and practice. I'm working on a web app that works with the spotify api, and I'm trying to figure out best practices for authorization. Spotify recommends only doing the authorization workflow from a backend service for data security, so I'm building that in flask. I'm trying to figure out the best way for my angular app to make requests and get an authorization token from my backend when there are external api's and multiple flask routes invloved.

my current flow works like this:

  • Angular app (running on localhost:4200) auth.service makes a redirect call to my backend at 'localhost:5000/call_spotify'
  • Flask makes a request to spotify to get an auth code, and sets the callback as localhost:5000/callback
  • flask receives the callback in the matching route, then uses requests.post() to make an api call to request a token
  • I get the token back inside the view function for '/callback', and then redirect to 'localhost:4200/auth?' with the tokens passed as query parameters
  • a component associated with the /auth route in my angular app gets the query parameters and then passes them to the auth.service to set the users tokens

This works right now, but I feel like it's the wrong way to go about it. I'm not entirely sure how I should be doing this kind of a flow because I've ever done it before. Any tips/ recommendations would be really appreciated!

routes.py:

@app.route('/test_spotify_call')
def test_spotify_call():

    params = {'response_type':'code',
        'client_id':CLIENT_ID,
        'scope':SCOPE,
        'redirect_uri': REDIRECT_URI}


    return redirect(f'https://accounts.spotify.com/authorize?{urlencode(params)}')



@app.route('/callback')
def resp_callback():
    print(request.headers)
    print(request.args.get('code'))


    post_data =  {
        'code':request.args.get('code'),
        'redirect_uri': REDIRECT_URI,
        'grant_type':'authorization_code'
    }


    auth_string = f'{CLIENT_ID}:{CLIENT_SECRET}'.encode('utf-8')
    b64_encoded = b64encode(auth_string).decode('utf-8')

    headers = {
        'Authorization': f'Basic {b64_encoded}'
    }

    res = requests.post('https://accounts.spotify.com/api/token',data=post_data, headers=headers)

    data = res.json()

    return redirect(f'http://localhost:4200/auth?{urlencode(data)}')

auth.component.ts:

export class AuthComponent implements OnInit {

    auth_object: AuthObject = {} ;

    constructor(private router: Router, 
        private route: ActivatedRoute, private auth: AuthService) { }

    ngOnInit(): void {
        console.log(this.route.snapshot.queryParamMap.get('token'))

        this.route.queryParams.subscribe( params => {
            console.log(params['access_token'])
            this.auth_object.access_token = params['access_token']
            this.auth_object.refresh_token = params['refresh_token']
            this.auth_object.expires_in =  params['expires_in']
            this.auth_object.token_type = params['token_type']
        })

        this.auth.set_data(this.auth_object);
        console.log(new Date() +  JSON.stringify(this.auth_object));

        this.router.navigate(['/events']);

    }

}

auth.service.ts:

@Injectable()
export class AuthService {


    private data: AuthObject;

    constructor(private http: HttpClient){}


    login():void{
        window.location.href = 'http://localhost:5000/test_spotify_call'

    }



    set_data(data: AuthObject):void{
        this.data = data;
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source